├── tutum ├── json_test_output │ ├── token.json │ ├── registry.json │ ├── webhook.json │ ├── webhook_array.json │ ├── volumegroup.json │ ├── volume.json │ ├── nodecluster.json │ ├── stack.json │ ├── listregistries.json │ ├── provider.json │ ├── nodetype.json │ ├── listwebhooks.json │ ├── region.json │ ├── liststacks.json │ ├── listvolumes.json │ ├── listvolumegroups.json │ ├── action.json │ ├── node.json │ ├── listproviders.json │ ├── listnodetypes.json │ ├── listregions.json │ ├── listactions.json │ ├── listnodes.json │ ├── service.json │ ├── image.json │ ├── listnodeclusters.json │ ├── container.json │ ├── imagetags.json │ ├── listservices.json │ ├── listimages.json │ └── listcontainers.json ├── token.go ├── http_test.go ├── token_test.go ├── imagetags_test.go ├── testmain.go ├── az.go ├── http.go ├── registries.go ├── node_type.go ├── imagetags.go ├── volume.go ├── region.go ├── provider.go ├── volume_group.go ├── node_test.go ├── stack_test.go ├── images_test.go ├── action_test.go ├── volume_test.go ├── provider_test.go ├── container_test.go ├── registries_test.go ├── region_test.go ├── node_type_test.go ├── volume_group_test.go ├── images.go ├── stream.go ├── service_test.go ├── node_cluster_test.go ├── node.go ├── action.go ├── trigger.go ├── tutum.go ├── node_cluster.go ├── stack.go ├── container.go ├── service.go └── type.go ├── circle.yml ├── .gitignore ├── README.md └── LICENSE /tutum/json_test_output/token.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "21268a001bcf40acb5c0884679d4fd1f" 3 | } 4 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | test: 2 | pre: 3 | - go get github.com/BurntSushi/toml 4 | - go get github.com/gorilla/websocket 5 | override: 6 | - go test -v ./... -race 7 | -------------------------------------------------------------------------------- /tutum/json_test_output/registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "r-staging.tutum.co", 3 | "icon_url": "/_static/assets/images/dockerregistries/tutum.png", 4 | "is_ssl": true, 5 | "is_tutum_registry": true, 6 | "name": "Tutum registry", 7 | "port": null, 8 | "resource_uri": "/api/v1/registry/r-staging.tutum.co/" 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | test.go 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | -------------------------------------------------------------------------------- /tutum/json_test_output/webhook.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mywebhook_name", 3 | "operation": "REDEPLOY", 4 | "resource_uri": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/webhook/handler/62a5f41d-192d-4c83-9982-4d3cbead1904/", 5 | "url": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/webhook/handler/62a5f41d-192d-4c83-9982-4d3cbead1904/call/", 6 | "uuid": "62a5f41d-192d-4c83-9982-4d3cbead1904" 7 | } 8 | -------------------------------------------------------------------------------- /tutum/json_test_output/webhook_array.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "name": "mywebhook_name", 3 | "operation": "REDEPLOY", 4 | "resource_uri": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/webhook/handler/62a5f41d-192d-4c83-9982-4d3cbead1904/", 5 | "url": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/webhook/handler/62a5f41d-192d-4c83-9982-4d3cbead1904/call/", 6 | "uuid": "62a5f41d-192d-4c83-9982-4d3cbead1904" 7 | }] 8 | -------------------------------------------------------------------------------- /tutum/json_test_output/volumegroup.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mysql_etc", 3 | "resource_uri": "/api/v1/volume/1863e34d-6a7d-4945-aefc-8f27a4ab1a9e/", 4 | "services": [ 5 | "/api/v1/service/1ddb524a-e7f5-4b68-b99c-50bf7c57602d/" 6 | ], 7 | "state": "Created", 8 | "uuid": "1863e34d-6a7d-4945-aefc-8f27a4ab1a9e", 9 | "volumes": [ 10 | "/api/v1/volume/1863e34d-6a7d-4945-aefc-8f27a4ab1a9e/" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tutum/json_test_output/volume.json: -------------------------------------------------------------------------------- 1 | { 2 | "containers": [ 3 | "/api/v1/container/55b07a2e-fa72-481f-984b-0e94f275de0b/" 4 | ], 5 | "node": "/api/v1/node/fc1a5bb9-17f5-4819-b667-8c7cd819e950/", 6 | "resource_uri": "/api/v1/volume/1863e34d-6a7d-4945-aefc-8f27a4ab1a9e/", 7 | "state": "Created", 8 | "uuid": "1863e34d-6a7d-4945-aefc-8f27a4ab1a9e", 9 | "volume_group": "/api/v1/volumegroup/2f4f54e5-9d3b-4ac1-85ad-a2d4ff25a173/" 10 | } 11 | -------------------------------------------------------------------------------- /tutum/token.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func CreateToken 7 | Returns : Token JSON object 8 | */ 9 | func CreateToken() (Token, error) { 10 | url := "token/" 11 | request := "POST" 12 | body := []byte(`{}`) 13 | var response Token 14 | 15 | data, err := TutumCall(url, request, body) 16 | if err != nil { 17 | return response, err 18 | } 19 | 20 | err = json.Unmarshal(data, &response) 21 | if err != nil { 22 | return response, err 23 | } 24 | 25 | return response, nil 26 | } 27 | -------------------------------------------------------------------------------- /tutum/json_test_output/nodecluster.json: -------------------------------------------------------------------------------- 1 | { 2 | "current_num_nodes": 0, 3 | "deployed_datetime": "Fri, 24 Apr 2015 15:37:56 +0000", 4 | "destroyed_datetime": null, 5 | "disk": 60, 6 | "name": "my_cluster", 7 | "node_type": "/api/v1/nodetype/digitalocean/1gb/", 8 | "region": "/api/v1/region/digitalocean/lon1/", 9 | "resource_uri": "/api/v1/nodecluster/2bc0ea8f-6edf-4f19-9c33-35c15c3e4250/", 10 | "state": "Empty cluster", 11 | "target_num_nodes": 0, 12 | "uuid": "2bc0ea8f-6edf-4f19-9c33-35c15c3e4250" 13 | } 14 | -------------------------------------------------------------------------------- /tutum/json_test_output/stack.json: -------------------------------------------------------------------------------- 1 | { 2 | "deployed_datetime": "Mon, 13 Oct 2014 11:01:43 +0000", 3 | "destroyed_datetime": null, 4 | "name": "tutum-app", 5 | "resource_uri": "/api/v1/stack/7fe7ec85-58be-4904-81da-de2219098d7c/", 6 | "services": [ 7 | "/api/v1/service/7fe7ec85-58be-4904-81da-de2219098d7c/", 8 | "/api/v1/service/7fe7ec85-58be-4904-81da-de2219098d7d/", 9 | "/api/v1/service/7fe7ec85-58be-4904-81da-de2219098d7e/" 10 | ], 11 | "state": "Running", 12 | "synchronized": true, 13 | "uuid": "09cbcf8d-a727-40d9-b420-c8e18b7fa55b" 14 | } 15 | -------------------------------------------------------------------------------- /tutum/http_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func Test_SetBaseUrl(t *testing.T) { 9 | 10 | url := "" 11 | 12 | os.Setenv("TUTUM_REST_HOST", "https://dashboard.tutum.co") 13 | url = SetBaseUrl() 14 | if url != "https://dashboard.tutum.co/api/v1/" { 15 | t.Fatal("Wrong url set") 16 | } 17 | os.Setenv("TUTUM_REST_HOST", "") 18 | os.Setenv("TUTUM_BASE_URL", "https://dashboard.tutum.co/api/v1/") 19 | url = SetBaseUrl() 20 | if url != "https://dashboard.tutum.co/api/v1/" { 21 | t.Fatal("Wrong url set") 22 | } 23 | os.Setenv("TUTUM_BASE_URL", "") 24 | } 25 | -------------------------------------------------------------------------------- /tutum/json_test_output/listregistries.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 2 8 | }, 9 | "objects": [ 10 | { 11 | "host": "r-staging.tutum.co", 12 | "icon_url": "/_static/assets/images/dockerregistries/tutum.png", 13 | "is_ssl": true, 14 | "is_tutum_registry": true, 15 | "name": "Tutum registry", 16 | "port": null, 17 | "resource_uri": "/api/v1/registry/r-staging.tutum.co/" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tutum/json_test_output/provider.json: -------------------------------------------------------------------------------- 1 | { 2 | "available": true, 3 | "label": "Digital Ocean", 4 | "name": "digitalocean", 5 | "regions": [ 6 | "/api/v1/region/digitalocean/ams1/", 7 | "/api/v1/region/digitalocean/ams2/", 8 | "/api/v1/region/digitalocean/ams3/", 9 | "/api/v1/region/digitalocean/lon1/", 10 | "/api/v1/region/digitalocean/nyc1/", 11 | "/api/v1/region/digitalocean/nyc2/", 12 | "/api/v1/region/digitalocean/nyc3/", 13 | "/api/v1/region/digitalocean/sfo1/", 14 | "/api/v1/region/digitalocean/sgp1/" 15 | ], 16 | "resource_uri": "/api/v1/provider/digitalocean/" 17 | } 18 | -------------------------------------------------------------------------------- /tutum/json_test_output/nodetype.json: -------------------------------------------------------------------------------- 1 | { 2 | "availability_zones": [], 3 | "available": true, 4 | "label": "1GB", 5 | "name": "1gb", 6 | "provider": "/api/v1/provider/digitalocean/", 7 | "regions": [ 8 | "/api/v1/region/digitalocean/ams1/", 9 | "/api/v1/region/digitalocean/sfo1/", 10 | "/api/v1/region/digitalocean/nyc2/", 11 | "/api/v1/region/digitalocean/ams2/", 12 | "/api/v1/region/digitalocean/sgp1/", 13 | "/api/v1/region/digitalocean/lon1/", 14 | "/api/v1/region/digitalocean/nyc3/", 15 | "/api/v1/region/digitalocean/nyc1/" 16 | ], 17 | "resource_uri": "/api/v1/nodetype/digitalocean/1gb/" 18 | } 19 | -------------------------------------------------------------------------------- /tutum/json_test_output/listwebhooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 1 8 | }, 9 | "objects": [ 10 | { 11 | "name": "mywebhook_name", 12 | "operation": "REDEPLOY", 13 | "resource_uri": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/webhook/handler/62a5f41d-192d-4c83-9982-4d3cbead1904/", 14 | "url": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/webhook/handler/62a5f41d-192d-4c83-9982-4d3cbead1904/call/", 15 | "uuid": "62a5f41d-192d-4c83-9982-4d3cbead1904" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tutum/json_test_output/region.json: -------------------------------------------------------------------------------- 1 | { 2 | "availability_zones": [], 3 | "available": true, 4 | "label": "Amsterdam 2", 5 | "name": "ams2", 6 | "node_types": [ 7 | "/api/v1/nodetype/digitalocean/1gb/", 8 | "/api/v1/nodetype/digitalocean/2gb/", 9 | "/api/v1/nodetype/digitalocean/4gb/", 10 | "/api/v1/nodetype/digitalocean/8gb/", 11 | "/api/v1/nodetype/digitalocean/16gb/", 12 | "/api/v1/nodetype/digitalocean/32gb/", 13 | "/api/v1/nodetype/digitalocean/48gb/", 14 | "/api/v1/nodetype/digitalocean/64gb/" 15 | ], 16 | "provider": "/api/v1/provider/digitalocean/", 17 | "resource_uri": "/api/v1/region/digitalocean/ams2/" 18 | } 19 | -------------------------------------------------------------------------------- /tutum/json_test_output/liststacks.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 0 8 | }, 9 | "objects": [ 10 | { 11 | "deployed_datetime": "Mon, 13 Oct 2014 11:01:43 +0000", 12 | "destroyed_datetime": null, 13 | "name": "tutum-app", 14 | "resource_uri": "/api/v1/stack/7fe7ec85-58be-4904-81da-de2219098d7c/", 15 | "services": ["/api/v1/service/7fe7ec85-58be-4904-81da-de2219098d7c/"], 16 | "state": "Running", 17 | "synchronized": true, 18 | "uuid": "09cbcf8d-a727-40d9-b420-c8e18b7fa55b" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tutum/json_test_output/listvolumes.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 1 8 | }, 9 | "objects": [ 10 | { 11 | "containers": [ 12 | "/api/v1/container/55b07a2e-fa72-481f-984b-0e94f275de0b/" 13 | ], 14 | "node": "/api/v1/node/fc1a5bb9-17f5-4819-b667-8c7cd819e950/", 15 | "resource_uri": "/api/v1/volume/1863e34d-6a7d-4945-aefc-8f27a4ab1a9e/", 16 | "state": "Created", 17 | "uuid": "1863e34d-6a7d-4945-aefc-8f27a4ab1a9e", 18 | "volume_group": "/api/v1/volumegroup/2f4f54e5-9d3b-4ac1-85ad-a2d4ff25a173/" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tutum/json_test_output/listvolumegroups.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 1 8 | }, 9 | "objects": [ 10 | { 11 | "name": "mysql_etc", 12 | "resource_uri": "/api/v1/volume/1863e34d-6a7d-4945-aefc-8f27a4ab1a9e/", 13 | "services": [ 14 | "/api/v1/service/1ddb524a-e7f5-4b68-b99c-50bf7c57602d/" 15 | ], 16 | "state": "Created", 17 | "uuid": "1863e34d-6a7d-4945-aefc-8f27a4ab1a9e", 18 | "volumes": [ 19 | "/api/v1/volume/1863e34d-6a7d-4945-aefc-8f27a4ab1a9e/" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tutum/json_test_output/action.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": "Cluster Create", 3 | "end_date": "Wed, 17 Sep 2014 08:26:22 +0000", 4 | "ip": "56.78.90.12", 5 | "location": "New York, USA", 6 | "logs": "", 7 | "method": "POST", 8 | "object": "/api/v1/cluster/eea638f4-b77a-4183-b241-22dbd7866f22/", 9 | "path": "/api/v1/cluster/", 10 | "resource_uri": "/api/v1/action/6246c558-976c-4df6-ba60-eb1a344a17af/", 11 | "start_date": "Wed, 17 Sep 2014 08:26:22 +0000", 12 | "state": "Success", 13 | "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.78.2 (KHTML, like Gecko) Version/7.0.6 Safari/537.78.2", 14 | "uuid": "6246c558-976c-4df6-ba60-eb1a344a17af", 15 | "body": "{\"image_tag\": \"/api/v1/image/tutum/ubuntu-quantal/tag/latest/\", \"name\": \"test_cluster\"}" 16 | } 17 | -------------------------------------------------------------------------------- /tutum/json_test_output/node.json: -------------------------------------------------------------------------------- 1 | { 2 | "current_num_containers": 2, 3 | "deployed_datetime": "Fri, 24 Apr 2015 16:17:05 +0000", 4 | "destroyed_datetime": null, 5 | "disk": 30, 6 | "docker_execdriver": "native-0.2", 7 | "docker_graphdriver": "aufs", 8 | "docker_version": "1.5.0", 9 | "external_fqdn": "89226618-maximeheckel.node.tutum.io", 10 | "last_seen": "Mon, 27 Apr 2015 12:17:05 +0000", 11 | "node_cluster": "/api/v1/nodecluster/72a7902a-5f70-4771-bcbf-4abb3a4f93fe/", 12 | "node_type": "/api/v1/nodetype/digitalocean/1gb/", 13 | "public_ip": "95.85.49.197", 14 | "region": "/api/v1/region/digitalocean/ams2/", 15 | "resource_uri": "/api/v1/node/89226618-4cbf-44a7-b354-0edd6e251068/", 16 | "state": "Deployed", 17 | "uuid": "89226618-4cbf-44a7-b354-0edd6e251068" 18 | } 19 | -------------------------------------------------------------------------------- /tutum/json_test_output/listproviders.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 7 8 | }, 9 | "objects": [ 10 | { 11 | "available": true, 12 | "label": "Digital Ocean", 13 | "name": "digitalocean", 14 | "regions": [ 15 | "/api/v1/region/digitalocean/ams1/", 16 | "/api/v1/region/digitalocean/ams2/", 17 | "/api/v1/region/digitalocean/ams3/", 18 | "/api/v1/region/digitalocean/lon1/", 19 | "/api/v1/region/digitalocean/nyc1/", 20 | "/api/v1/region/digitalocean/nyc2/", 21 | "/api/v1/region/digitalocean/nyc3/", 22 | "/api/v1/region/digitalocean/sfo1/", 23 | "/api/v1/region/digitalocean/sgp1/" 24 | ], 25 | "resource_uri": "/api/v1/provider/digitalocean/" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /tutum/json_test_output/listnodetypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 159 8 | }, 9 | "objects": [ 10 | { 11 | "availability_zones": [], 12 | "available": true, 13 | "label": "1GB", 14 | "name": "1gb", 15 | "provider": "/api/v1/provider/digitalocean/", 16 | "regions": [ 17 | "/api/v1/region/digitalocean/ams1/", 18 | "/api/v1/region/digitalocean/sfo1/", 19 | "/api/v1/region/digitalocean/nyc2/", 20 | "/api/v1/region/digitalocean/ams2/", 21 | "/api/v1/region/digitalocean/sgp1/", 22 | "/api/v1/region/digitalocean/lon1/", 23 | "/api/v1/region/digitalocean/nyc3/", 24 | "/api/v1/region/digitalocean/nyc1/" 25 | ], 26 | "resource_uri": "/api/v1/nodetype/digitalocean/1gb/" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tutum/json_test_output/listregions.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 41 8 | }, 9 | "objects": [ 10 | { 11 | "availability_zones": [], 12 | "available": true, 13 | "label": "Amsterdam 2", 14 | "name": "ams2", 15 | "node_types": [ 16 | "/api/v1/nodetype/digitalocean/1gb/", 17 | "/api/v1/nodetype/digitalocean/2gb/", 18 | "/api/v1/nodetype/digitalocean/4gb/", 19 | "/api/v1/nodetype/digitalocean/8gb/", 20 | "/api/v1/nodetype/digitalocean/16gb/", 21 | "/api/v1/nodetype/digitalocean/32gb/", 22 | "/api/v1/nodetype/digitalocean/48gb/", 23 | "/api/v1/nodetype/digitalocean/64gb/" 24 | ], 25 | "provider": "/api/v1/provider/digitalocean/", 26 | "resource_uri": "/api/v1/region/digitalocean/ams2/" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tutum/json_test_output/listactions.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 133 8 | }, 9 | "objects": [ 10 | { 11 | "action": "Cluster Create", 12 | "end_date": "Wed, 17 Sep 2014 08:26:22 +0000", 13 | "ip": "56.78.90.12", 14 | "location": "New York, USA", 15 | "logs": "", 16 | "method": "POST", 17 | "object": "/api/v1/cluster/eea638f4-b77a-4183-b241-22dbd7866f22/", 18 | "path": "/api/v1/cluster/", 19 | "resource_uri": "/api/v1/action/6246c558-976c-4df6-ba60-eb1a344a17af/", 20 | "start_date": "Wed, 17 Sep 2014 08:26:22 +0000", 21 | "state": "Success", 22 | "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.78.2 (KHTML, like Gecko) Version/7.0.6 Safari/537.78.2", 23 | "uuid": "6246c558-976c-4df6-ba60-eb1a344a17af", 24 | "body": "{\"image_tag\": \"/api/v1/image/tutum/ubuntu-quantal/tag/latest/\", \"name\": \"test_cluster\"}" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tutum/json_test_output/listnodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 1 8 | }, 9 | "objects": [ 10 | { 11 | "current_num_containers": 2, 12 | "deployed_datetime": "Fri, 24 Apr 2015 16:17:05 +0000", 13 | "destroyed_datetime": null, 14 | "disk": 30, 15 | "docker_execdriver": "native-0.2", 16 | "docker_graphdriver": "aufs", 17 | "docker_version": "1.5.0", 18 | "external_fqdn": "89226618-maximeheckel.node.tutum.io", 19 | "last_seen": "Mon, 27 Apr 2015 12:17:05 +0000", 20 | "node_cluster": "/api/v1/nodecluster/72a7902a-5f70-4771-bcbf-4abb3a4f93fe/", 21 | "node_type": "/api/v1/nodetype/digitalocean/1gb/", 22 | "public_ip": "95.85.49.197", 23 | "region": "/api/v1/region/digitalocean/ams2/", 24 | "resource_uri": "/api/v1/node/89226618-4cbf-44a7-b354-0edd6e251068/", 25 | "state": "Deployed", 26 | "uuid": "89226618-4cbf-44a7-b354-0edd6e251068" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /tutum/json_test_output/service.json: -------------------------------------------------------------------------------- 1 | { 2 | "autodestroy": "OFF", 3 | "autoredeploy": false, 4 | "autorestart": "OFF", 5 | "container_ports": [], 6 | "cpu_shares": null, 7 | "current_num_containers": 2, 8 | "deployed_datetime": "Fri, 24 Apr 2015 15:43:52 +0000", 9 | "deployment_strategy": "EMPTIEST_NODE", 10 | "destroyed_datetime": null, 11 | "entrypoint": null, 12 | "image_name": "tutum/hello-world:latest", 13 | "image_tag": "/api/v1/image/tutum/hello-world/tag/latest/", 14 | "memory": null, 15 | "name": "my-new-app", 16 | "privileged": false, 17 | "public_dns": "my-new-app.maximeheckel.svc.tutum.io", 18 | "resource_uri": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/", 19 | "run_command": null, 20 | "running_num_containers": 2, 21 | "sequential_deployment": false, 22 | "stack": null, 23 | "started_datetime": "Fri, 24 Apr 2015 16:30:56 +0000", 24 | "state": "Running", 25 | "stopped_datetime": "Fri, 24 Apr 2015 15:44:08 +0000", 26 | "stopped_num_containers": 0, 27 | "synchronized": true, 28 | "target_num_containers": 2, 29 | "uuid": "02522970-a79a-46d6-8a64-475bf52e4258" 30 | } 31 | -------------------------------------------------------------------------------- /tutum/token_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_CreateToken(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("token.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/token/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response Token 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := CreateToken() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tutum/imagetags_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_GetImageTag(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("imagetags.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | w.WriteHeader(200) 24 | w.Header().Set("Content-Type", "application/json") 25 | fmt.Fprintln(w, fake_response) 26 | })) 27 | 28 | defer server.Close() 29 | url := server.URL + "/api/v1/image/" + fake_image_name + "/tag/" + fake_image_tag + "/" 30 | 31 | res, err := http.Get(url) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | body, err := ioutil.ReadAll(res.Body) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | var response ImageTags 42 | err = json.Unmarshal(body, &response) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | BaseUrl = server.URL + "/api/v1/" 48 | test_response, err := GetImageTag(fake_image_name, fake_image_tag) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | if reflect.DeepEqual(test_response, response) != true { 53 | t.Fatal("Invalid output") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tutum/json_test_output/image.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 23 8 | }, 9 | "objects": [ 10 | { 11 | "build_source": null, 12 | "categories": [ 13 | { 14 | "name": "Database servers" 15 | } 16 | ], 17 | "description": "MySQL Server image - listens in port 3306. For the admin account password, either set MYSQL_PASS environment variable, or check the logs for a randomly generated one.", 18 | "icon_url": "/_static/assets/images/dockerimages/mysql-64.png", 19 | "in_use": false, 20 | "is_private_image": false, 21 | "jumpstart": true, 22 | "last_build_date": "2015-06-19 15:12:04+00:00", 23 | "name": "tutum/mysql", 24 | "public_url": "https://registry.hub.docker.com/u/tutum/mysql/", 25 | "registry": "/api/v1/registry/registry.hub.docker.com/", 26 | "resource_uri": "/api/v1/image/tutum/mysql/", 27 | "star_count": 124, 28 | "state": "Success", 29 | "tags": [ 30 | "/api/v1/image/tutum/mysql/tag/latest/" 31 | ] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /tutum/testmain.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | /* 10 | Test variables used as arguments 11 | */ 12 | var ( 13 | fake_location_name = "ams2" 14 | fake_name = "1gb" 15 | fake_provider = "digitalocean" 16 | fake_uuid_action = "6246c558-976c-4df6-ba60-eb1a344a17af" 17 | fake_uuid_container = "dcbe16b4-21a1-474b-a814-131a3626b1de" 18 | fake_uuid_node = "89226618-4cbf-44a7-b354-0edd6e251068" 19 | fake_uuid_nodecluster = "72a7902a-5f70-4771-bcbf-4abb3a4f93fe" 20 | fake_uuid_service = "02522970-a79a-46d6-8a64-475bf52e4258" 21 | fake_uuid_stack = "09cbcf8d-a727-40d9-b420-c8e18b7fa55b" 22 | fake_uuid_volume = "1863e34d-6a7d-4945-aefc-8f27a4ab1a9e" 23 | fake_uuid_volumegroup = "1863e34d-6a7d-4945-aefc-8f27a4ab1a9e" 24 | fake_image_name = "tutum/mysql" 25 | fake_image_tag = "latest" 26 | ) 27 | 28 | /* 29 | func MockupResponse 30 | Argument : endpoint 31 | Returns : the corresponding JSON response file 32 | */ 33 | func MockupResponse(response_file string) (string, error) { 34 | 35 | file, e := ioutil.ReadFile("json_test_output/" + response_file) 36 | if e != nil { 37 | fmt.Printf("File error: %v\n", e) 38 | os.Exit(1) 39 | } 40 | 41 | fake_response := string(file) 42 | 43 | return fake_response, nil 44 | } 45 | -------------------------------------------------------------------------------- /tutum/json_test_output/listnodeclusters.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 2 8 | }, 9 | "objects": [ 10 | { 11 | "current_num_nodes": 0, 12 | "deployed_datetime": "Fri, 24 Apr 2015 15:37:56 +0000", 13 | "destroyed_datetime": null, 14 | "disk": 60, 15 | "name": "my_cluster", 16 | "node_type": "/api/v1/nodetype/digitalocean/1gb/", 17 | "region": "/api/v1/region/digitalocean/lon1/", 18 | "resource_uri": "/api/v1/nodecluster/2bc0ea8f-6edf-4f19-9c33-35c15c3e4250/", 19 | "state": "Empty cluster", 20 | "target_num_nodes": 0, 21 | "uuid": "2bc0ea8f-6edf-4f19-9c33-35c15c3e4250" 22 | }, 23 | { 24 | "current_num_nodes": 1, 25 | "deployed_datetime": "Fri, 24 Apr 2015 16:17:05 +0000", 26 | "destroyed_datetime": null, 27 | "disk": 60, 28 | "name": "Test-Cluster", 29 | "node_type": "/api/v1/nodetype/digitalocean/1gb/", 30 | "region": "/api/v1/region/digitalocean/ams2/", 31 | "resource_uri": "/api/v1/nodecluster/72a7902a-5f70-4771-bcbf-4abb3a4f93fe/", 32 | "state": "Deployed", 33 | "target_num_nodes": 1, 34 | "uuid": "72a7902a-5f70-4771-bcbf-4abb3a4f93fe" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /tutum/az.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | func ListAZ() (AZListResponse, error) { 6 | url := "az/" 7 | request := "GET" 8 | //Empty Body Request 9 | body := []byte(`{}`) 10 | var response AZListResponse 11 | var finalResponse AZListResponse 12 | 13 | data, err := TutumCall(url, request, body) 14 | if err != nil { 15 | return response, err 16 | } 17 | 18 | err = json.Unmarshal(data, &response) 19 | if err != nil { 20 | return response, err 21 | } 22 | 23 | finalResponse = response 24 | 25 | Loop: 26 | for { 27 | if response.Meta.Next != "" { 28 | var nextResponse AZListResponse 29 | data, err := TutumCall(response.Meta.Next[8:], request, body) 30 | if err != nil { 31 | return nextResponse, err 32 | } 33 | err = json.Unmarshal(data, &nextResponse) 34 | if err != nil { 35 | return nextResponse, err 36 | } 37 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 38 | response = nextResponse 39 | 40 | } else { 41 | break Loop 42 | } 43 | } 44 | 45 | return finalResponse, nil 46 | } 47 | 48 | func GetAZ(az string) (AZ, error) { 49 | 50 | url := "az/" + az + "/" 51 | request := "GET" 52 | //Empty Body Request 53 | body := []byte(`{}`) 54 | var response AZ 55 | 56 | data, err := TutumCall(url, request, body) 57 | if err != nil { 58 | return response, err 59 | } 60 | 61 | err = json.Unmarshal(data, &response) 62 | if err != nil { 63 | return response, err 64 | } 65 | 66 | return response, nil 67 | } 68 | -------------------------------------------------------------------------------- /tutum/http.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | var customUserAgent = "go-tutum/" + version 12 | 13 | func SetUserAgent(name string) string { 14 | customUserAgent = "" 15 | customUserAgent = name + " go-tutum/" + version 16 | return customUserAgent 17 | } 18 | 19 | func SetBaseUrl() string { 20 | if os.Getenv("TUTUM_REST_HOST") != "" { 21 | BaseUrl = os.Getenv("TUTUM_REST_HOST") 22 | BaseUrl = BaseUrl + "/api/v1/" 23 | } else if os.Getenv("TUTUM_BASE_URL") != "" { 24 | BaseUrl = os.Getenv("TUTUM_BASE_URL") 25 | } 26 | return BaseUrl 27 | } 28 | 29 | func TutumCall(url string, requestType string, requestBody []byte) ([]byte, error) { 30 | 31 | if !IsAuthenticated() { 32 | return nil, fmt.Errorf("Couldn't find any Tutum credentials in ~/.tutum or environment variables TUTUM_USER and TUTUM_APIKEY") 33 | } 34 | 35 | BaseUrl = SetBaseUrl() 36 | 37 | client := &http.Client{} 38 | req, err := http.NewRequest(requestType, BaseUrl+url, bytes.NewBuffer(requestBody)) 39 | 40 | req.Header.Add("Authorization", AuthHeader) 41 | req.Header.Add("Accept", "application/json") 42 | req.Header.Add("User-Agent", customUserAgent) 43 | 44 | response, err := client.Do(req) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | if response.StatusCode > 300 { 50 | return nil, fmt.Errorf("Failed API call: %s ", response.Status) 51 | } 52 | 53 | data, err := ioutil.ReadAll(response.Body) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return data, nil 59 | } 60 | -------------------------------------------------------------------------------- /tutum/json_test_output/container.json: -------------------------------------------------------------------------------- 1 | { 2 | "autodestroy": "OFF", 3 | "autorestart": "OFF", 4 | "container_ports": [ 5 | { 6 | "endpoint_uri": null, 7 | "inner_port": 80, 8 | "outer_port": null, 9 | "port_name": "http", 10 | "protocol": "tcp", 11 | "published": false, 12 | "uri_protocol": "http" 13 | } 14 | ], 15 | "cpu_shares": null, 16 | "deployed_datetime": "Fri, 24 Apr 2015 16:30:47 +0000", 17 | "destroyed_datetime": null, 18 | "docker_id": "9ab356fd7aa7b7e23f31a0eb952e7c31ff4182136767370e15cb6321651a0885", 19 | "entrypoint": "", 20 | "exit_code": null, 21 | "exit_code_msg": null, 22 | "image_name": "tutum/hello-world:latest", 23 | "image_tag": "/api/v1/image/tutum/hello-world/tag/latest/", 24 | "layer": "/api/v1/layer/529c404c672f7370316815c0ccd6b544a8b1baf72741a3de601457a58f205cf9/", 25 | "memory": null, 26 | "name": "my-new-app-1", 27 | "node": "/api/v1/node/89226618-4cbf-44a7-b354-0edd6e251068/", 28 | "private_ip": "10.7.0.1", 29 | "privileged": false, 30 | "public_dns": "my-new-app-1.maximeheckel.cont.tutum.io", 31 | "resource_uri": "/api/v1/container/dcbe16b4-21a1-474b-a814-131a3626b1de/", 32 | "run_command": "/run.sh", 33 | "service": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/", 34 | "started_datetime": "Fri, 24 Apr 2015 16:30:47 +0000", 35 | "state": "Running", 36 | "stopped_datetime": null, 37 | "synchronized": true, 38 | "uuid": "dcbe16b4-21a1-474b-a814-131a3626b1de" 39 | } 40 | -------------------------------------------------------------------------------- /tutum/registries.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | ) 7 | 8 | func ListRegistries() (RegistryListResponse, error) { 9 | url := "registry/" 10 | request := "GET" 11 | //Empty Body Request 12 | body := []byte(`{}`) 13 | var response RegistryListResponse 14 | var finalResponse RegistryListResponse 15 | 16 | data, err := TutumCall(url, request, body) 17 | if err != nil { 18 | return response, err 19 | } 20 | 21 | err = json.Unmarshal(data, &response) 22 | if err != nil { 23 | log.Println("hello") 24 | return response, err 25 | } 26 | 27 | finalResponse = response 28 | 29 | Loop: 30 | for { 31 | if response.Meta.Next != "" { 32 | var nextResponse RegistryListResponse 33 | data, err := TutumCall(response.Meta.Next[8:], request, body) 34 | if err != nil { 35 | return nextResponse, err 36 | } 37 | err = json.Unmarshal(data, &nextResponse) 38 | if err != nil { 39 | return nextResponse, err 40 | } 41 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 42 | response = nextResponse 43 | 44 | } else { 45 | break Loop 46 | } 47 | } 48 | 49 | return finalResponse, nil 50 | } 51 | 52 | func GetRegistry(registry string) (Registry, error) { 53 | 54 | url := "registry/" + registry + "/" 55 | request := "GET" 56 | //Empty Body Request 57 | body := []byte(`{}`) 58 | var response Registry 59 | 60 | data, err := TutumCall(url, request, body) 61 | if err != nil { 62 | return response, err 63 | } 64 | 65 | err = json.Unmarshal(data, &response) 66 | if err != nil { 67 | return response, err 68 | } 69 | 70 | return response, nil 71 | } 72 | -------------------------------------------------------------------------------- /tutum/node_type.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListNodeTypes 7 | Returns : Array of NodeType objects 8 | */ 9 | func ListNodeTypes() (NodeTypeListResponse, error) { 10 | 11 | url := "nodetype/" 12 | request := "GET" 13 | 14 | //Empty Body Request 15 | body := []byte(`{}`) 16 | var response NodeTypeListResponse 17 | var finalResponse NodeTypeListResponse 18 | 19 | data, err := TutumCall(url, request, body) 20 | if err != nil { 21 | return response, err 22 | } 23 | 24 | err = json.Unmarshal(data, &response) 25 | if err != nil { 26 | return response, err 27 | } 28 | 29 | finalResponse = response 30 | 31 | Loop: 32 | for { 33 | if response.Meta.Next != "" { 34 | var nextResponse NodeTypeListResponse 35 | data, err := TutumCall(response.Meta.Next[8:], request, body) 36 | if err != nil { 37 | return nextResponse, err 38 | } 39 | err = json.Unmarshal(data, &nextResponse) 40 | if err != nil { 41 | return nextResponse, err 42 | } 43 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 44 | response = nextResponse 45 | 46 | } else { 47 | break Loop 48 | } 49 | } 50 | 51 | return response, nil 52 | } 53 | 54 | /* 55 | func GetNodeType 56 | Argument : provider name and type name 57 | Returns : NodeType JSON object 58 | */ 59 | func GetNodeType(provider string, name string) (NodeType, error) { 60 | url := "nodetype/" + provider + "/" + name + "/" 61 | request := "GET" 62 | body := []byte(`{}`) 63 | var response NodeType 64 | 65 | data, err := TutumCall(url, request, body) 66 | if err != nil { 67 | return response, err 68 | } 69 | 70 | err = json.Unmarshal(data, &response) 71 | if err != nil { 72 | return response, err 73 | } 74 | 75 | return response, nil 76 | } 77 | -------------------------------------------------------------------------------- /tutum/imagetags.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | ) 7 | 8 | func GetImageTag(name string, tag string) (ImageTags, error) { 9 | url := "" 10 | if string(name[0]) == "/" { 11 | url = name[8:] 12 | } else { 13 | url = "image/" + name + "/tag/" + tag + "/" 14 | } 15 | 16 | request := "GET" 17 | //Empty Body Request 18 | body := []byte(`{}`) 19 | var response ImageTags 20 | 21 | data, err := TutumCall(url, request, body) 22 | if err != nil { 23 | return response, err 24 | } 25 | 26 | err = json.Unmarshal(data, &response) 27 | if err != nil { 28 | return response, err 29 | } 30 | 31 | return response, nil 32 | } 33 | 34 | func GetImageBuildSetting(name string, tag string) (BuildSettings, error) { 35 | 36 | url := "" 37 | if string(name[0]) == "/" { 38 | url = name[8:] 39 | } else { 40 | url = "image/" + name + "/buildsetting/" + tag + "/" 41 | } 42 | 43 | log.Println(url) 44 | 45 | request := "GET" 46 | //Empty Body Request 47 | body := []byte(`{}`) 48 | var response BuildSettings 49 | 50 | data, err := TutumCall(url, request, body) 51 | if err != nil { 52 | return response, err 53 | } 54 | 55 | err = json.Unmarshal(data, &response) 56 | if err != nil { 57 | return response, err 58 | } 59 | 60 | return response, nil 61 | 62 | } 63 | 64 | func (self *BuildSettings) Build() (BuildSettings, error) { 65 | url := "image/" + self.Image[14:len(self.Image)-1] + "/buildsetting/" + self.Tag + "/build/" 66 | request := "POST" 67 | 68 | body := []byte(`{}`) 69 | var response BuildSettings 70 | 71 | data, err := TutumCall(url, request, body) 72 | if err != nil { 73 | return response, err 74 | } 75 | 76 | err = json.Unmarshal(data, &response) 77 | if err != nil { 78 | return response, err 79 | } 80 | 81 | return response, nil 82 | } 83 | -------------------------------------------------------------------------------- /tutum/volume.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListVolumes 7 | Returns : Array of Volume objects 8 | */ 9 | func ListVolumes() (VolumeListResponse, error) { 10 | url := "volume/" 11 | request := "GET" 12 | //Empty Body Request 13 | body := []byte(`{}`) 14 | var response VolumeListResponse 15 | var finalResponse VolumeListResponse 16 | 17 | data, err := TutumCall(url, request, body) 18 | if err != nil { 19 | return response, err 20 | } 21 | 22 | err = json.Unmarshal(data, &response) 23 | if err != nil { 24 | return response, err 25 | } 26 | 27 | finalResponse = response 28 | 29 | Loop: 30 | for { 31 | if response.Meta.Next != "" { 32 | var nextResponse VolumeListResponse 33 | data, err := TutumCall(response.Meta.Next[8:], request, body) 34 | if err != nil { 35 | return nextResponse, err 36 | } 37 | err = json.Unmarshal(data, &nextResponse) 38 | if err != nil { 39 | return nextResponse, err 40 | } 41 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 42 | response = nextResponse 43 | 44 | } else { 45 | break Loop 46 | } 47 | } 48 | 49 | return finalResponse, nil 50 | } 51 | 52 | /* 53 | func GetVolume 54 | Argument : uuid 55 | Returns : Volume JSON object 56 | */ 57 | func GetVolume(uuid string) (Volume, error) { 58 | 59 | url := "" 60 | if string(uuid[0]) == "/" { 61 | url = uuid[8:] 62 | } else { 63 | url = "volume/" + uuid + "/" 64 | } 65 | 66 | request := "GET" 67 | //Empty Body Request 68 | body := []byte(`{}`) 69 | var response Volume 70 | 71 | data, err := TutumCall(url, request, body) 72 | if err != nil { 73 | return response, err 74 | } 75 | 76 | err = json.Unmarshal(data, &response) 77 | if err != nil { 78 | return response, err 79 | } 80 | 81 | return response, nil 82 | } 83 | -------------------------------------------------------------------------------- /tutum/region.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListRegions 7 | Returns : Array of Region objects 8 | */ 9 | func ListRegions() (RegionListResponse, error) { 10 | 11 | url := "region/" 12 | request := "GET" 13 | //Empty Body Request 14 | body := []byte(`{}`) 15 | var response RegionListResponse 16 | var finalResponse RegionListResponse 17 | 18 | data, err := TutumCall(url, request, body) 19 | if err != nil { 20 | return response, err 21 | } 22 | 23 | err = json.Unmarshal(data, &response) 24 | if err != nil { 25 | return response, err 26 | } 27 | 28 | finalResponse = response 29 | 30 | Loop: 31 | for { 32 | if response.Meta.Next != "" { 33 | var nextResponse RegionListResponse 34 | data, err := TutumCall(response.Meta.Next[8:], request, body) 35 | if err != nil { 36 | return nextResponse, err 37 | } 38 | err = json.Unmarshal(data, &nextResponse) 39 | if err != nil { 40 | return nextResponse, err 41 | } 42 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 43 | response = nextResponse 44 | 45 | } else { 46 | break Loop 47 | } 48 | } 49 | 50 | return finalResponse, nil 51 | } 52 | 53 | /* 54 | func GetRegion 55 | Argument : provider name and location name 56 | Returns : Region JSON object 57 | */ 58 | func GetRegion(id string) (Region, error) { 59 | 60 | url := "" 61 | if string(id[0]) == "/" { 62 | url = id[8:] 63 | } else { 64 | url = "region/" + id + "/" 65 | } 66 | 67 | request := "GET" 68 | //Empty Body Request 69 | body := []byte(`{}`) 70 | var response Region 71 | 72 | data, err := TutumCall(url, request, body) 73 | if err != nil { 74 | return response, err 75 | } 76 | 77 | err = json.Unmarshal(data, &response) 78 | if err != nil { 79 | return response, err 80 | } 81 | 82 | return response, nil 83 | } 84 | -------------------------------------------------------------------------------- /tutum/provider.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListProviders 7 | Returns : Array of Provider objects 8 | */ 9 | func ListProviders() (ProviderListResponse, error) { 10 | 11 | url := "provider/" 12 | request := "GET" 13 | //Empty Body Request 14 | body := []byte(`{}`) 15 | var response ProviderListResponse 16 | var finalResponse ProviderListResponse 17 | 18 | data, err := TutumCall(url, request, body) 19 | if err != nil { 20 | return response, err 21 | } 22 | 23 | err = json.Unmarshal(data, &response) 24 | if err != nil { 25 | return response, err 26 | } 27 | 28 | finalResponse = response 29 | 30 | Loop: 31 | for { 32 | if response.Meta.Next != "" { 33 | var nextResponse ProviderListResponse 34 | data, err := TutumCall(response.Meta.Next[8:], request, body) 35 | if err != nil { 36 | return nextResponse, err 37 | } 38 | err = json.Unmarshal(data, &nextResponse) 39 | if err != nil { 40 | return nextResponse, err 41 | } 42 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 43 | response = nextResponse 44 | 45 | } else { 46 | break Loop 47 | } 48 | } 49 | 50 | return finalResponse, nil 51 | } 52 | 53 | /* 54 | func GetProvider 55 | Argument : name of the provider 56 | Returns : Provider JSON object 57 | */ 58 | func GetProvider(name string) (Provider, error) { 59 | 60 | url := "" 61 | if string(name[0]) == "/" { 62 | url = name[8:] 63 | } else { 64 | url = "provider/" + name + "/" 65 | } 66 | 67 | request := "GET" 68 | //Empty Body Request 69 | body := []byte(`{}`) 70 | var response Provider 71 | 72 | data, err := TutumCall(url, request, body) 73 | if err != nil { 74 | return response, err 75 | } 76 | 77 | err = json.Unmarshal(data, &response) 78 | if err != nil { 79 | return response, err 80 | } 81 | 82 | return response, nil 83 | } 84 | -------------------------------------------------------------------------------- /tutum/volume_group.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListVolumeGroups 7 | Returns : Array of VolumeGroup objects 8 | */ 9 | func ListVolumeGroups() (VolumeGroupListResponse, error) { 10 | 11 | url := "volumegroup/" 12 | request := "GET" 13 | //Empty Body Request 14 | body := []byte(`{}`) 15 | var response VolumeGroupListResponse 16 | var finalResponse VolumeGroupListResponse 17 | 18 | data, err := TutumCall(url, request, body) 19 | if err != nil { 20 | return response, err 21 | } 22 | 23 | err = json.Unmarshal(data, &response) 24 | if err != nil { 25 | return response, err 26 | } 27 | 28 | finalResponse = response 29 | 30 | Loop: 31 | for { 32 | if response.Meta.Next != "" { 33 | var nextResponse VolumeGroupListResponse 34 | data, err := TutumCall(response.Meta.Next[8:], request, body) 35 | if err != nil { 36 | return nextResponse, err 37 | } 38 | err = json.Unmarshal(data, &nextResponse) 39 | if err != nil { 40 | return nextResponse, err 41 | } 42 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 43 | response = nextResponse 44 | 45 | } else { 46 | break Loop 47 | } 48 | } 49 | 50 | return finalResponse, nil 51 | } 52 | 53 | /* 54 | func GetVolumeGroup 55 | Argument : uuid 56 | Returns : VolumeGroup JSON object 57 | */ 58 | func GetVolumeGroup(uuid string) (VolumeGroup, error) { 59 | 60 | url := "" 61 | if string(uuid[0]) == "/" { 62 | url = uuid[8:] 63 | } else { 64 | url = "volumegroup/" + uuid + "/" 65 | } 66 | 67 | request := "GET" 68 | //Empty Body Request 69 | body := []byte(`{}`) 70 | var response VolumeGroup 71 | 72 | data, err := TutumCall(url, request, body) 73 | if err != nil { 74 | return response, err 75 | } 76 | 77 | err = json.Unmarshal(data, &response) 78 | if err != nil { 79 | return response, err 80 | } 81 | 82 | return response, nil 83 | } 84 | -------------------------------------------------------------------------------- /tutum/json_test_output/imagetags.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildable": false, 3 | "full_name": "tutum/mysql:latest", 4 | "image": "/api/v1/image/tutum/mysql/", 5 | "layer": { 6 | "author": "Fernando Mayo , Feng Honglin ", 7 | "creation": "Tue, 7 Jul 2015 15:14:04 +0000", 8 | "docker_id": "033a71bff2be14f7b7c1db05c6e76cb483b8d59b9df085bc2dbe8ec48f97a9a1", 9 | "entrypoint": "", 10 | "envvars": [ 11 | { 12 | "key": "MYSQL_PASS", 13 | "value": "**Random**" 14 | }, 15 | { 16 | "key": "MYSQL_USER", 17 | "value": "admin" 18 | }, 19 | { 20 | "key": "ON_CREATE_DB", 21 | "value": "**False**" 22 | }, 23 | { 24 | "key": "PATH", 25 | "value": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 26 | }, 27 | { 28 | "key": "REPLICATION_MASTER", 29 | "value": "**False**" 30 | }, 31 | { 32 | "key": "REPLICATION_PASS", 33 | "value": "replica" 34 | }, 35 | { 36 | "key": "REPLICATION_SLAVE", 37 | "value": "**False**" 38 | }, 39 | { 40 | "key": "REPLICATION_USER", 41 | "value": "replica" 42 | } 43 | ], 44 | "labels": [], 45 | "ports": [ 46 | { 47 | "port": 3306, 48 | "protocol": "tcp" 49 | } 50 | ], 51 | "resource_uri": "/api/v1/layer/033a71bff2be14f7b7c1db05c6e76cb483b8d59b9df085bc2dbe8ec48f97a9a1/", 52 | "run_command": "/run.sh", 53 | "volumes": [ 54 | { 55 | "container_path": "/etc/mysql" 56 | }, 57 | { 58 | "container_path": "/var/lib/mysql" 59 | } 60 | ] 61 | }, 62 | "name": "latest", 63 | "resource_uri": "/api/v1/image/tutum/mysql/tag/latest/", 64 | "state": "Success" 65 | } 66 | -------------------------------------------------------------------------------- /tutum/node_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListNodes(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listnodes.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | w.WriteHeader(200) 24 | w.Header().Set("Content-Type", "application/json") 25 | fmt.Fprintln(w, fake_response) 26 | })) 27 | 28 | defer server.Close() 29 | url := server.URL + "/api/v1/node/" 30 | 31 | res, err := http.Get(url) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | body, err := ioutil.ReadAll(res.Body) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | var response NodeListResponse 42 | err = json.Unmarshal(body, &response) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | BaseUrl = server.URL + "/api/v1/" 48 | 49 | test_response, err := ListNodes() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetNode(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("node.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/node/" + fake_uuid_node 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Node 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetNode(fake_uuid_node) 94 | 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | if reflect.DeepEqual(test_response, response) != true { 99 | t.Fatal("Invalid output") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tutum/stack_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListStacks(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("liststacks.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/stack/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response StackListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListStacks() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetStack(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("stack.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/stack/" + fake_uuid_stack 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Stack 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetStack(fake_uuid_stack) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/images_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListImages(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listimages.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | w.WriteHeader(200) 24 | w.Header().Set("Content-Type", "application/json") 25 | fmt.Fprintln(w, fake_response) 26 | })) 27 | 28 | defer server.Close() 29 | url := server.URL + "/api/v1/image/" 30 | 31 | res, err := http.Get(url) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | body, err := ioutil.ReadAll(res.Body) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | var response ImageListResponse 42 | err = json.Unmarshal(body, &response) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | BaseUrl = server.URL + "/api/v1/" 48 | 49 | test_response, err := ListImages() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | if reflect.DeepEqual(test_response, response) != true { 55 | t.Fatal("Invalid output") 56 | } 57 | } 58 | 59 | func Test_GetImage(t *testing.T) { 60 | User = "test" 61 | ApiKey = "test" 62 | 63 | fake_response, err := MockupResponse("image.json") 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 69 | w.WriteHeader(200) 70 | w.Header().Set("Content-Type", "application/json") 71 | fmt.Fprintln(w, fake_response) 72 | })) 73 | 74 | defer server.Close() 75 | url := server.URL + "/api/v1/image/" + fake_image_name 76 | 77 | res, err := http.Get(url) 78 | if err != nil { 79 | t.Fatal(err) 80 | } 81 | 82 | body, err := ioutil.ReadAll(res.Body) 83 | if err != nil { 84 | t.Fatal(err) 85 | } 86 | 87 | var response Image 88 | err = json.Unmarshal(body, &response) 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | 93 | BaseUrl = server.URL + "/api/v1/" 94 | test_response, err := GetImage(fake_image_name) 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | if reflect.DeepEqual(test_response, response) != true { 99 | t.Fatal("Invalid output") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tutum/action_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListActions(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listactions.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/action/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response ActionListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListActions() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetAction(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("action.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/action/" + fake_uuid_action 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Action 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetAction(fake_uuid_action) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/volume_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListVolumes(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listvolumes.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/volume/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response VolumeListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListVolumes() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetVolume(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("volume.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/volume/" + fake_uuid_volume 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Volume 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetVolume(fake_uuid_volume) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/provider_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListProviders(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listproviders.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/provider/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response ProviderListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListProviders() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetProvider(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("provider.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/provider/" + fake_provider + "/" 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Provider 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetProvider(fake_provider) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/container_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListContainers(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listcontainers.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | w.WriteHeader(200) 24 | w.Header().Set("Content-Type", "application/json") 25 | fmt.Fprintln(w, fake_response) 26 | })) 27 | 28 | defer server.Close() 29 | url := server.URL + "/api/v1/container/" 30 | 31 | res, err := http.Get(url) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | body, err := ioutil.ReadAll(res.Body) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | var response CListResponse 42 | err = json.Unmarshal(body, &response) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | BaseUrl = server.URL + "/api/v1/" 48 | 49 | test_response, err := ListContainers() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetContainer(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("container.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/container/" + fake_uuid_container 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Container 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetContainer(fake_uuid_container) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/registries_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListRegistries(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listregistries.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | w.WriteHeader(200) 24 | w.Header().Set("Content-Type", "application/json") 25 | fmt.Fprintln(w, fake_response) 26 | })) 27 | 28 | defer server.Close() 29 | url := server.URL + "/api/v1/registry/" 30 | 31 | res, err := http.Get(url) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | body, err := ioutil.ReadAll(res.Body) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | var response RegistryListResponse 42 | err = json.Unmarshal(body, &response) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | BaseUrl = server.URL + "/api/v1/" 48 | 49 | test_response, err := ListRegistries() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetRegistry(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("registry.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/registry/" + fake_uuid_container 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Registry 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetRegistry(fake_uuid_container) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/region_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListRegions(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listregions.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/region/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response RegionListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListRegions() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetRegion(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("region.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/region/" + fake_provider + "/" + fake_location_name + "/" 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Region 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetRegion(fake_provider + "/" + fake_name) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/node_type_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListNodeTypes(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listnodetypes.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/nodetype/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response NodeTypeListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListNodeTypes() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetNodeType(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("nodetype.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/nodetype/" + fake_provider + "/" + fake_name + "/" 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response NodeType 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetNodeType(fake_provider, fake_name) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | 98 | if reflect.DeepEqual(test_response, response) != true { 99 | t.Fatal("Invalid output") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tutum/volume_group_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListVolumeGroups(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listvolumegroups.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/volumegroup/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response VolumeGroupListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListVolumeGroups() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetVolumeGroup(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("volumegroup.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/volumegroup/" + fake_uuid_volumegroup 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response VolumeGroup 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetVolumeGroup(fake_uuid_volumegroup) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tutum/images.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | func ListImages() (ImageListResponse, error) { 6 | url := "image/" 7 | request := "GET" 8 | //Empty Body Request 9 | body := []byte(`{}`) 10 | var response ImageListResponse 11 | var finalResponse ImageListResponse 12 | 13 | data, err := TutumCall(url, request, body) 14 | if err != nil { 15 | return response, err 16 | } 17 | err = json.Unmarshal(data, &response) 18 | if err != nil { 19 | return response, err 20 | } 21 | 22 | finalResponse = response 23 | 24 | Loop: 25 | for { 26 | if response.Meta.Next != "" { 27 | var nextResponse ImageListResponse 28 | data, err := TutumCall(response.Meta.Next[8:], request, body) 29 | if err != nil { 30 | return nextResponse, err 31 | } 32 | err = json.Unmarshal(data, &nextResponse) 33 | if err != nil { 34 | return nextResponse, err 35 | } 36 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 37 | response = nextResponse 38 | 39 | } else { 40 | break Loop 41 | } 42 | } 43 | 44 | return finalResponse, nil 45 | } 46 | 47 | func GetImage(name string) (Image, error) { 48 | 49 | url := "" 50 | if string(name[0]) == "/" { 51 | url = name[8:] 52 | } else { 53 | url = "image/" + name + "/" 54 | } 55 | 56 | request := "GET" 57 | //Empty Body Request 58 | body := []byte(`{}`) 59 | var response Image 60 | 61 | data, err := TutumCall(url, request, body) 62 | if err != nil { 63 | return response, err 64 | } 65 | 66 | err = json.Unmarshal(data, &response) 67 | if err != nil { 68 | return response, err 69 | } 70 | 71 | return response, nil 72 | 73 | } 74 | 75 | func CreateImage(createRequest ImageCreateRequest) (Image, error) { 76 | 77 | url := "image/" 78 | request := "POST" 79 | var response Image 80 | 81 | newImage, err := json.Marshal(createRequest) 82 | if err != nil { 83 | return response, err 84 | } 85 | 86 | data, err := TutumCall(url, request, newImage) 87 | if err != nil { 88 | return response, err 89 | } 90 | 91 | err = json.Unmarshal(data, &response) 92 | if err != nil { 93 | return response, err 94 | } 95 | 96 | return response, nil 97 | } 98 | 99 | func (self *Image) Update(createRequest ImageCreateRequest) error { 100 | 101 | url := "image/" + self.Name + "/" 102 | request := "PATCH" 103 | 104 | updatedImage, err := json.Marshal(createRequest) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | _, err = TutumCall(url, request, updatedImage) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | return nil 115 | } 116 | 117 | func (self *Image) Remove() error { 118 | url := "image/" + self.Name + "/" 119 | request := "DELETE" 120 | //Empty Body Request 121 | body := []byte(`{}`) 122 | 123 | _, err := TutumCall(url, request, body) 124 | if err != nil { 125 | return err 126 | } 127 | 128 | return nil 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-tutum 2 | ======== 3 | 4 | Go library for Tutum's API. Full documentation available at [https://docs.tutum.co/v2/api/?golang](https://docs.tutum.co/v2/api/?golang 5 | ) 6 | 7 | ##Set up 8 | 9 | **Installation:** 10 | 11 | In order to install the Tutum Go library, you can use : 12 | 13 | go get github.com/tutumcloud/go-tutum/tutum 14 | 15 | 16 | **Auth:** 17 | 18 | In order to be able to make requests to the API, you should first obtain an ApiKey for your account. For this, log into Tutum, click on the menu on the upper right corner of the screen and select **Get Api Key**. 19 | 20 | You can use your ApiKey with the Go library in any of the following ways: 21 | 22 | Manually set in your Go code 23 | 24 | tutum.User = "yourUsernameHere" 25 | tutum.ApiKey = "yourApiKeyHere" 26 | 27 | Store in a config file in ~/.tutum 28 | 29 | [auth] 30 | user = "username" 31 | apikey = "apikey" 32 | 33 | Set the environment variables TUTUM_USER and TUTUM_APIKEY 34 | 35 | ##Examples 36 | 37 | 38 | **Note** 39 | 40 | Each of the methods that require a uuid number as argument can also use a resource_uri as argument. 41 | 42 | **Creating and deploying a NodeCluster** 43 | 44 | ``` 45 | nodecluster, err := tutum.CreateNodeCluster(tutum.NodeCreateRequest{Name: "Go-SDK-test", Region: "/api/v1/region/digitalocean/lon1/", NodeType: "/api/v1/nodetype/digitalocean/1gb/", Target_num_nodes: 2}) 46 | 47 | if err != nil { 48 | log.Println(err) 49 | } 50 | 51 | if err = nodecluster.Deploy(); err != nil { 52 | log.Println(err) 53 | } 54 | ``` 55 | 56 | **Creating and starting a Stack** 57 | 58 | ``` 59 | stack, err := tutum.CreateStack(tutum.StackCreateRequest{Name: "new-stack", Services: []tutum.ServiceCreateRequest{{Image: "tutum/hello-world", Name: "test", Target_num_containers: 2}}}) 60 | 61 | if err != nil { 62 | log.Println(err) 63 | } 64 | 65 | if err = stack.Start(); err != nil { 66 | log.Println(err) 67 | } 68 | ``` 69 | 70 | **Listing running containers** 71 | 72 | ``` 73 | containers, err := tutum.ListContainers() 74 | 75 | if err != nil { 76 | log.Println(err) 77 | } 78 | 79 | log.Println(containers) 80 | ``` 81 | 82 | **Stopping a running service** 83 | 84 | ``` 85 | service, err := tutum.GetService("7eaf7fff-882c-4f3d-9a8f-a22317ac00ce") 86 | // or service, err := tutum.GetService("/api/v1/service/7eaf7fff-882c-4f3d-9a8f-a22317ac00ce") 87 | 88 | 89 | if err != nil { 90 | log.Println(err) 91 | } 92 | 93 | if err = service.Stop(); err != nil { 94 | log.Println(err) 95 | } 96 | ``` 97 | 98 | 99 | **Events** 100 | 101 | In order to handle events, you can call the TutumEvents function inside a goroutine. 102 | 103 | ``` 104 | tutum.StreamUrl = "wss://stream.tutum.co:443/v1/" 105 | 106 | c := make(chan tutum.Event) 107 | e := make(chan error) 108 | go tutum.TutumEvents(c, e) 109 | 110 | for { 111 | select { 112 | case event := <-c: 113 | log.Println(event) 114 | case err := <-e: 115 | log.Println(err) 116 | } 117 | } 118 | ``` 119 | 120 | The complete API Documentation is available [here](https://docs.tutum.co/v2/api/) with additional examples written in Go. 121 | -------------------------------------------------------------------------------- /tutum/stream.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "net/http" 7 | "net/url" 8 | "os" 9 | "reflect" 10 | "time" 11 | 12 | "github.com/gorilla/websocket" 13 | ) 14 | 15 | const ( 16 | // Time allowed to write a message to the peer. 17 | WRITE_WAIT = 5 * time.Second 18 | // Time allowed to read the next pong message from the peer. 19 | PONG_WAIT = 10 * time.Second 20 | // Send pings to client with this period. Must be less than PONG_WAIT. 21 | PING_PERIOD = PONG_WAIT / 2 22 | ) 23 | 24 | /* 25 | func dial() 26 | Returns : a websocket connection 27 | */ 28 | 29 | func dial() (*websocket.Conn, error) { 30 | LoadAuth() 31 | 32 | if os.Getenv("TUTUM_STREAM_HOST") != "" { 33 | u, _ := url.Parse(os.Getenv("TUTUM_STREAM_HOST")) 34 | _, port, _ := net.SplitHostPort(u.Host) 35 | if port == "" { 36 | u.Host = u.Host + ":443" 37 | } 38 | StreamUrl = u.Scheme + "://" + u.Host + "/v1/" 39 | } else if os.Getenv("TUTUM_STREAM_URL") != "" { 40 | u, _ := url.Parse(os.Getenv("TUTUM_STREAM_URL")) 41 | _, port, _ := net.SplitHostPort(u.Host) 42 | if port == "" { 43 | u.Host = u.Host + ":443" 44 | } 45 | StreamUrl = u.Scheme + "://" + u.Host + "/v1/" 46 | } 47 | 48 | Url := StreamUrl + "events/" 49 | 50 | header := http.Header{} 51 | header.Add("Authorization", AuthHeader) 52 | header.Add("User-Agent", customUserAgent) 53 | 54 | var Dialer websocket.Dialer 55 | ws, _, err := Dialer.Dial(Url, header) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return ws, nil 61 | } 62 | 63 | func dialHandler(e chan error) *websocket.Conn { 64 | tries := 0 65 | for { 66 | ws, err := dial() 67 | if err != nil { 68 | tries++ 69 | time.Sleep(3 * time.Second) 70 | if tries > 3 { 71 | log.Println("[DIAL ERROR]: " + err.Error()) 72 | e <- err 73 | } 74 | } else { 75 | return ws 76 | } 77 | } 78 | } 79 | 80 | func messagesHandler(ws *websocket.Conn, ticker *time.Ticker, msg Event, c chan Event, e chan error, e2 chan error) { 81 | ws.SetPongHandler(func(string) error { 82 | ws.SetReadDeadline(time.Now().Add(PONG_WAIT)) 83 | return nil 84 | }) 85 | for { 86 | err := ws.ReadJSON(&msg) 87 | if err != nil { 88 | e <- err 89 | e2 <- err 90 | time.Sleep(4 * time.Second) 91 | } else { 92 | if reflect.TypeOf(msg).String() == "tutum.Event" { 93 | c <- msg 94 | } 95 | } 96 | } 97 | } 98 | 99 | /* 100 | func TutumStreamCall 101 | Returns : The stream of all events from your NodeClusters, Containers, Services, Stack, Actions, ... 102 | */ 103 | 104 | func TutumEvents(c chan Event, e chan error) { 105 | var msg Event 106 | ticker := time.NewTicker(PING_PERIOD) 107 | ws := dialHandler(e) 108 | 109 | e2 := make(chan error) 110 | 111 | defer func() { 112 | close(c) 113 | close(e) 114 | ws.Close() 115 | }() 116 | go messagesHandler(ws, ticker, msg, c, e, e2) 117 | 118 | Loop: 119 | for { 120 | select { 121 | case <-ticker.C: 122 | if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { 123 | ticker.Stop() 124 | log.Println("Ping Timeout") 125 | e <- err 126 | break Loop 127 | } 128 | case <-e2: 129 | ticker.Stop() 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /tutum/json_test_output/listservices.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 2 8 | }, 9 | "objects": [ 10 | { 11 | "autodestroy": "OFF", 12 | "autoredeploy": false, 13 | "autorestart": "OFF", 14 | "container_ports": [ 15 | { 16 | "endpoint_uri": null, 17 | "inner_port": 3000, 18 | "outer_port": null, 19 | "port_name": "unknown", 20 | "protocol": "tcp", 21 | "published": true 22 | } 23 | ], 24 | "cpu_shares": null, 25 | "current_num_containers": 0, 26 | "deployed_datetime": "Mon, 20 Apr 2015 11:16:39 +0000", 27 | "deployment_strategy": "EMPTIEST_NODE", 28 | "destroyed_datetime": null, 29 | "entrypoint": null, 30 | "image_name": "tutum.co/maximeheckel/quicksell-image-resampler-server:latest", 31 | "image_tag": "/api/v1/image/tutum.co/maximeheckel/quicksell-image-resampler-server/tag/latest/", 32 | "memory": null, 33 | "name": "image-resampler", 34 | "privileged": false, 35 | "public_dns": "image-resampler.maximeheckel.svc.tutum.io", 36 | "resource_uri": "/api/v1/service/1a1775a3-5ed8-40cb-8b4f-f934c0a879e2/", 37 | "run_command": null, 38 | "running_num_containers": 0, 39 | "sequential_deployment": false, 40 | "stack": null, 41 | "started_datetime": "Wed, 22 Apr 2015 13:07:11 +0000", 42 | "state": "Not running", 43 | "stopped_datetime": "Wed, 22 Apr 2015 12:52:44 +0000", 44 | "stopped_num_containers": 0, 45 | "synchronized": true, 46 | "target_num_containers": 1, 47 | "uuid": "1a1775a3-5ed8-40cb-8b4f-f934c0a879e2" 48 | }, 49 | { 50 | "autodestroy": "OFF", 51 | "autoredeploy": false, 52 | "autorestart": "OFF", 53 | "container_ports": [], 54 | "cpu_shares": null, 55 | "current_num_containers": 2, 56 | "deployed_datetime": "Fri, 24 Apr 2015 15:43:52 +0000", 57 | "deployment_strategy": "EMPTIEST_NODE", 58 | "destroyed_datetime": null, 59 | "entrypoint": null, 60 | "image_name": "tutum/hello-world:latest", 61 | "image_tag": "/api/v1/image/tutum/hello-world/tag/latest/", 62 | "memory": null, 63 | "name": "my-new-app", 64 | "privileged": false, 65 | "public_dns": "my-new-app.maximeheckel.svc.tutum.io", 66 | "resource_uri": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/", 67 | "run_command": null, 68 | "running_num_containers": 2, 69 | "sequential_deployment": false, 70 | "stack": null, 71 | "started_datetime": "Fri, 24 Apr 2015 16:30:56 +0000", 72 | "state": "Running", 73 | "stopped_datetime": "Fri, 24 Apr 2015 15:44:08 +0000", 74 | "stopped_num_containers": 0, 75 | "synchronized": true, 76 | "target_num_containers": 2, 77 | "uuid": "02522970-a79a-46d6-8a64-475bf52e4258" 78 | } 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /tutum/json_test_output/listimages.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 23 8 | }, 9 | "objects": [ 10 | { 11 | "build_source": null, 12 | "categories": [ 13 | { 14 | "name": "Database servers" 15 | } 16 | ], 17 | "description": "MySQL Server image - listens in port 3306. For the admin account password, either set MYSQL_PASS environment variable, or check the logs for a randomly generated one.", 18 | "icon_url": "/_static/assets/images/dockerimages/mysql-64.png", 19 | "in_use": false, 20 | "is_private_image": false, 21 | "jumpstart": true, 22 | "last_build_date": "2015-06-19 15:12:04+00:00", 23 | "name": "tutum/mysql", 24 | "public_url": "https://registry.hub.docker.com/u/tutum/mysql/", 25 | "registry": "/api/v1/registry/registry.hub.docker.com/", 26 | "resource_uri": "/api/v1/image/tutum/mysql/", 27 | "star_count": 124, 28 | "state": "Success", 29 | "tags": [ 30 | "/api/v1/image/tutum/mysql/tag/latest/" 31 | ] 32 | }, 33 | { 34 | "build_source": null, 35 | "categories": [ 36 | { 37 | "name": "Database servers" 38 | } 39 | ], 40 | "description": "InfluxDB image - listens in port 8083 (web) and 8086 (HTTP API). Please fill the port mapping to 8086 when using web console. Default credential: root:root. ", 41 | "icon_url": "/_static/assets/images/dockerimages/influxdb-64.png", 42 | "in_use": false, 43 | "is_private_image": false, 44 | "jumpstart": true, 45 | "last_build_date": "2015-06-22 17:14:10+00:00", 46 | "name": "tutum/influxdb", 47 | "public_url": "https://registry.hub.docker.com/u/tutum/influxdb/", 48 | "registry": "/api/v1/registry/registry.hub.docker.com/", 49 | "resource_uri": "/api/v1/image/tutum/influxdb/", 50 | "star_count": 60, 51 | "state": "Success", 52 | "tags": [ 53 | "/api/v1/image/tutum/influxdb/tag/latest/" 54 | ] 55 | }, 56 | { 57 | "build_source": null, 58 | "categories": [ 59 | { 60 | "name": "Messaging queues" 61 | } 62 | ], 63 | "description": "RabbitMQ Docker image – listens in ports 5672/15672 (admin). For the admin password, either set RABBITMQ_PASS environment variable or read the logs for a randomly generated one", 64 | "icon_url": "/_static/assets/images/dockerimages/rabbitmq-64.png", 65 | "in_use": false, 66 | "is_private_image": false, 67 | "jumpstart": true, 68 | "last_build_date": "2015-05-12 10:22:33+00:00", 69 | "name": "tutum/rabbitmq", 70 | "public_url": "https://registry.hub.docker.com/u/tutum/rabbitmq/", 71 | "registry": "/api/v1/registry/registry.hub.docker.com/", 72 | "resource_uri": "/api/v1/image/tutum/rabbitmq/", 73 | "star_count": 42, 74 | "state": "Success", 75 | "tags": [ 76 | "/api/v1/image/tutum/rabbitmq/tag/latest/" 77 | ] 78 | } 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /tutum/service_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListServices(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listservices.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(200) 23 | w.Header().Set("Content-Type", "application/json") 24 | fmt.Fprintln(w, fake_response) 25 | })) 26 | 27 | defer server.Close() 28 | url := server.URL + "/api/v1/service/" 29 | 30 | res, err := http.Get(url) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | body, err := ioutil.ReadAll(res.Body) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | 40 | var response SListResponse 41 | err = json.Unmarshal(body, &response) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | BaseUrl = server.URL + "/api/v1/" 47 | 48 | test_response, err := ListServices() 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetService(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("service.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/service/" + fake_uuid_service 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response Service 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetService(fake_uuid_service) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | 102 | func Test_CreateService(t *testing.T) { 103 | User = "test" 104 | ApiKey = "test" 105 | 106 | fake_response, err := MockupResponse("service.json") 107 | if err != nil { 108 | t.Fatal(err) 109 | } 110 | 111 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 112 | w.WriteHeader(200) 113 | w.Header().Set("Content-Type", "application/json") 114 | fmt.Fprintln(w, fake_response) 115 | })) 116 | 117 | defer server.Close() 118 | url := server.URL + "/api/v1/service/" 119 | 120 | res, err := http.Get(url) 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | 125 | body, err := ioutil.ReadAll(res.Body) 126 | if err != nil { 127 | t.Fatal(err) 128 | } 129 | 130 | var response Service 131 | err = json.Unmarshal(body, &response) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | 136 | BaseUrl = server.URL + "/api/v1/" 137 | test_response, err := CreateService(ServiceCreateRequest{Image: "tutum/hello-world", Name: "test", Target_num_containers: 2}) 138 | if err != nil { 139 | t.Fatal(err) 140 | } 141 | if reflect.DeepEqual(test_response, response) != true { 142 | t.Fatal("Invalid output") 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /tutum/node_cluster_test.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/http/httptest" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func Test_ListNodeClusters(t *testing.T) { 14 | User = "test" 15 | ApiKey = "test" 16 | 17 | fake_response, err := MockupResponse("listnodeclusters.json") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 | w.WriteHeader(200) 24 | w.Header().Set("Content-Type", "application/json") 25 | fmt.Fprintln(w, fake_response) 26 | })) 27 | 28 | defer server.Close() 29 | url := server.URL + "/api/v1/nodecluster/" 30 | 31 | res, err := http.Get(url) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | body, err := ioutil.ReadAll(res.Body) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | var response NodeClusterListResponse 42 | err = json.Unmarshal(body, &response) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | 47 | BaseUrl = server.URL + "/api/v1/" 48 | 49 | test_response, err := ListNodeClusters() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | if reflect.DeepEqual(test_response, response) != true { 54 | t.Fatal("Invalid output") 55 | } 56 | } 57 | 58 | func Test_GetNodeCluster(t *testing.T) { 59 | User = "test" 60 | ApiKey = "test" 61 | 62 | fake_response, err := MockupResponse("nodecluster.json") 63 | if err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(200) 69 | w.Header().Set("Content-Type", "application/json") 70 | fmt.Fprintln(w, fake_response) 71 | })) 72 | 73 | defer server.Close() 74 | url := server.URL + "/api/v1/nodecluster/" + fake_uuid_nodecluster 75 | 76 | res, err := http.Get(url) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | 81 | body, err := ioutil.ReadAll(res.Body) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | var response NodeCluster 87 | err = json.Unmarshal(body, &response) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | 92 | BaseUrl = server.URL + "/api/v1/" 93 | test_response, err := GetNodeCluster(fake_uuid_nodecluster) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | if reflect.DeepEqual(test_response, response) != true { 98 | t.Fatal("Invalid output") 99 | } 100 | } 101 | 102 | func Test_CreateNodeCluster(t *testing.T) { 103 | User = "test" 104 | ApiKey = "test" 105 | 106 | fake_response, err := MockupResponse("nodecluster.json") 107 | if err != nil { 108 | t.Fatal(err) 109 | } 110 | 111 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 112 | w.WriteHeader(200) 113 | w.Header().Set("Content-Type", "application/json") 114 | fmt.Fprintln(w, fake_response) 115 | })) 116 | 117 | defer server.Close() 118 | url := server.URL + "/api/v1/nodecluster/" 119 | 120 | res, err := http.Get(url) 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | 125 | body, err := ioutil.ReadAll(res.Body) 126 | if err != nil { 127 | t.Fatal(err) 128 | } 129 | 130 | var response NodeCluster 131 | err = json.Unmarshal(body, &response) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | 136 | BaseUrl = server.URL + "/api/v1/" 137 | test_response, err := CreateNodeCluster(NodeCreateRequest{Name: "Go-SDK-test", Region: "/api/v1/region/digitalocean/lon1/", NodeType: "/api/v1/nodetype/digitalocean/1gb/", Target_num_nodes: 2}) 138 | if err != nil { 139 | t.Fatal(err) 140 | } 141 | if reflect.DeepEqual(test_response, response) != true { 142 | t.Fatal("Invalid output") 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /tutum/json_test_output/listcontainers.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "limit": 25, 4 | "next": null, 5 | "offset": 0, 6 | "previous": null, 7 | "total_count": 2 8 | }, 9 | "objects": [ 10 | { 11 | "autodestroy": "OFF", 12 | "autorestart": "OFF", 13 | "container_ports": [ 14 | { 15 | "endpoint_uri": null, 16 | "inner_port": 80, 17 | "outer_port": null, 18 | "port_name": "http", 19 | "protocol": "tcp", 20 | "published": false, 21 | "uri_protocol": "http" 22 | } 23 | ], 24 | "cpu_shares": null, 25 | "deployed_datetime": "Fri, 24 Apr 2015 16:30:47 +0000", 26 | "destroyed_datetime": null, 27 | "docker_id": "9ab356fd7aa7b7e23f31a0eb952e7c31ff4182136767370e15cb6321651a0885", 28 | "entrypoint": "", 29 | "exit_code": null, 30 | "exit_code_msg": null, 31 | "image_name": "tutum/hello-world:latest", 32 | "image_tag": "/api/v1/image/tutum/hello-world/tag/latest/", 33 | "layer": "/api/v1/layer/529c404c672f7370316815c0ccd6b544a8b1baf72741a3de601457a58f205cf9/", 34 | "memory": null, 35 | "name": "my-new-app-1", 36 | "node": "/api/v1/node/89226618-4cbf-44a7-b354-0edd6e251068/", 37 | "private_ip": "10.7.0.1", 38 | "privileged": false, 39 | "public_dns": "my-new-app-1.maximeheckel.cont.tutum.io", 40 | "resource_uri": "/api/v1/container/dcbe16b4-21a1-474b-a814-131a3626b1de/", 41 | "run_command": "/run.sh", 42 | "service": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/", 43 | "started_datetime": "Fri, 24 Apr 2015 16:30:47 +0000", 44 | "state": "Running", 45 | "stopped_datetime": null, 46 | "synchronized": true, 47 | "uuid": "dcbe16b4-21a1-474b-a814-131a3626b1de" 48 | }, 49 | { 50 | "autodestroy": "OFF", 51 | "autorestart": "OFF", 52 | "container_ports": [ 53 | { 54 | "endpoint_uri": null, 55 | "inner_port": 80, 56 | "outer_port": null, 57 | "port_name": "http", 58 | "protocol": "tcp", 59 | "published": false, 60 | "uri_protocol": "http" 61 | } 62 | ], 63 | "cpu_shares": null, 64 | "deployed_datetime": "Fri, 24 Apr 2015 16:30:56 +0000", 65 | "destroyed_datetime": null, 66 | "docker_id": "1c09ffe60df381ff854f5625da632d152f109b0f2962be10bb43919d3de23fd4", 67 | "entrypoint": "", 68 | "exit_code": null, 69 | "exit_code_msg": null, 70 | "image_name": "tutum/hello-world:latest", 71 | "image_tag": "/api/v1/image/tutum/hello-world/tag/latest/", 72 | "layer": "/api/v1/layer/529c404c672f7370316815c0ccd6b544a8b1baf72741a3de601457a58f205cf9/", 73 | "memory": null, 74 | "name": "my-new-app-2", 75 | "node": "/api/v1/node/89226618-4cbf-44a7-b354-0edd6e251068/", 76 | "private_ip": "10.7.0.2", 77 | "privileged": false, 78 | "public_dns": "my-new-app-2.maximeheckel.cont.tutum.io", 79 | "resource_uri": "/api/v1/container/3d84762f-46de-4c6a-b81c-2a11923df9db/", 80 | "run_command": "/run.sh", 81 | "service": "/api/v1/service/02522970-a79a-46d6-8a64-475bf52e4258/", 82 | "started_datetime": "Fri, 24 Apr 2015 16:30:56 +0000", 83 | "state": "Running", 84 | "stopped_datetime": null, 85 | "synchronized": true, 86 | "uuid": "3d84762f-46de-4c6a-b81c-2a11923df9db" 87 | } 88 | ] 89 | } 90 | -------------------------------------------------------------------------------- /tutum/node.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | /* 12 | func ListNodes 13 | Returns : Array of Node objects 14 | */ 15 | func ListNodes() (NodeListResponse, error) { 16 | 17 | url := "node/" 18 | request := "GET" 19 | 20 | //Empty Body Request 21 | body := []byte(`{}`) 22 | var response NodeListResponse 23 | var finalResponse NodeListResponse 24 | 25 | data, err := TutumCall(url, request, body) 26 | if err != nil { 27 | return response, err 28 | } 29 | 30 | err = json.Unmarshal(data, &response) 31 | if err != nil { 32 | return response, err 33 | } 34 | 35 | finalResponse = response 36 | 37 | Loop: 38 | for { 39 | if response.Meta.Next != "" { 40 | var nextResponse NodeListResponse 41 | data, err := TutumCall(response.Meta.Next[8:], request, body) 42 | if err != nil { 43 | return nextResponse, err 44 | } 45 | err = json.Unmarshal(data, &nextResponse) 46 | if err != nil { 47 | return nextResponse, err 48 | } 49 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 50 | response = nextResponse 51 | 52 | } else { 53 | break Loop 54 | } 55 | } 56 | 57 | return finalResponse, nil 58 | } 59 | 60 | /* 61 | func GetNode 62 | Argument : uuid 63 | Returns : Node JSON object 64 | */ 65 | func GetNode(uuid string) (Node, error) { 66 | 67 | url := "" 68 | if string(uuid[0]) == "/" { 69 | url = uuid[8:] 70 | } else { 71 | url = "node/" + uuid + "/" 72 | } 73 | 74 | request := "GET" 75 | body := []byte(`{}`) 76 | var response Node 77 | 78 | data, err := TutumCall(url, request, body) 79 | if err != nil { 80 | return response, err 81 | } 82 | 83 | err = json.Unmarshal(data, &response) 84 | if err != nil { 85 | return response, err 86 | } 87 | 88 | return response, nil 89 | } 90 | 91 | /* 92 | func UpdateNode 93 | Argument : uuid 94 | Returns : Node JSON object 95 | */ 96 | func (self *Node) Update(createRequest Node) error { 97 | 98 | url := "node/" + self.Uuid + "/" 99 | request := "PATCH" 100 | 101 | updatedNode, err := json.Marshal(createRequest) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | _, errr := TutumCall(url, request, updatedNode) 107 | if err != nil { 108 | return errr 109 | } 110 | 111 | return nil 112 | } 113 | 114 | /* 115 | func UpgradeDaemon 116 | Argument : uuid 117 | Returns : Node JSON object 118 | */ 119 | func (self *Node) Upgrade() error { 120 | 121 | url := "node/" + self.Uuid + "/docker-upgrade/" 122 | request := "POST" 123 | //Empty Body Request 124 | body := []byte(`{}`) 125 | 126 | _, err := TutumCall(url, request, body) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | return nil 132 | } 133 | 134 | /* 135 | func TerminateNode 136 | Argument : uuid 137 | Returns : Node JSON object 138 | */ 139 | func (self *Node) Terminate() error { 140 | 141 | url := "node/" + self.Uuid + "/" 142 | request := "DELETE" 143 | //Empty Body Request 144 | body := []byte(`{}`) 145 | 146 | _, err := TutumCall(url, request, body) 147 | if err != nil { 148 | return err 149 | } 150 | 151 | return nil 152 | } 153 | 154 | func (self *Node) Events(c chan NodeEvent) { 155 | endpoint := "node/" + self.Uuid + "/events/?user=" + User + "&token=" + ApiKey 156 | url := StreamUrl + endpoint 157 | 158 | header := http.Header{} 159 | header.Add("User-Agent", customUserAgent) 160 | 161 | var Dialer websocket.Dialer 162 | ws, _, err := Dialer.Dial(url, header) 163 | if err != nil { 164 | log.Println(err) 165 | } 166 | 167 | var msg NodeEvent 168 | for { 169 | if err = ws.ReadJSON(&msg); err != nil { 170 | if err != nil && err.Error() != "EOF" { 171 | log.Println(err) 172 | } else { 173 | break 174 | } 175 | } 176 | c <- msg 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /tutum/action.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | /* 12 | func ListActions 13 | Returns : Array of Action objects 14 | */ 15 | func ListActions() (ActionListResponse, error) { 16 | url := "action/" 17 | request := "GET" 18 | //Empty Body Request 19 | body := []byte(`{}`) 20 | var response ActionListResponse 21 | var finalResponse ActionListResponse 22 | data, err := TutumCall(url, request, body) 23 | if err != nil { 24 | return response, err 25 | } 26 | 27 | err = json.Unmarshal(data, &response) 28 | if err != nil { 29 | return response, err 30 | } 31 | 32 | finalResponse = response 33 | 34 | Loop: 35 | for { 36 | if response.Meta.Next != "" { 37 | var nextResponse ActionListResponse 38 | data, err := TutumCall(response.Meta.Next[8:], request, body) 39 | if err != nil { 40 | return nextResponse, err 41 | } 42 | err = json.Unmarshal(data, &nextResponse) 43 | if err != nil { 44 | return nextResponse, err 45 | } 46 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 47 | response = nextResponse 48 | 49 | } else { 50 | break Loop 51 | } 52 | } 53 | 54 | return finalResponse, nil 55 | } 56 | 57 | /* 58 | func GetAction 59 | Argument : uuid 60 | Returns : Action JSON object 61 | */ 62 | func GetAction(uuid string) (Action, error) { 63 | 64 | url := "" 65 | if string(uuid[0]) == "/" { 66 | url = uuid[8:] 67 | } else { 68 | url = "action/" + uuid + "/" 69 | } 70 | 71 | request := "GET" 72 | //Empty Body Request 73 | body := []byte(`{}`) 74 | var response Action 75 | 76 | data, err := TutumCall(url, request, body) 77 | if err != nil { 78 | return response, err 79 | } 80 | 81 | err = json.Unmarshal(data, &response) 82 | if err != nil { 83 | return response, err 84 | } 85 | 86 | return response, nil 87 | } 88 | 89 | /* 90 | func GetLogs 91 | Argument : a channel of type string for the output 92 | */ 93 | 94 | func (self *Action) GetLogs(c chan Logs) { 95 | endpoint := "action/" + self.Uuid + "/logs/?user=" + User + "&token=" + ApiKey 96 | url := StreamUrl + endpoint 97 | 98 | header := http.Header{} 99 | header.Add("User-Agent", customUserAgent) 100 | 101 | var Dialer websocket.Dialer 102 | ws, _, err := Dialer.Dial(url, header) 103 | if err != nil { 104 | log.Println(err) 105 | } 106 | 107 | var msg Logs 108 | for { 109 | if err = ws.ReadJSON(&msg); err != nil { 110 | if err != nil && err.Error() != "EOF" { 111 | log.Println(err) 112 | } else { 113 | break 114 | } 115 | } 116 | c <- msg 117 | } 118 | } 119 | 120 | func (self *Action) Cancel() (Action, error) { 121 | url := "" 122 | if string(self.Uuid[0]) == "/" { 123 | url = self.Uuid[8:] 124 | } else { 125 | url = "action/" + self.Uuid + "/cancel/" 126 | } 127 | 128 | request := "POST" 129 | //Empty Body Request 130 | body := []byte(`{}`) 131 | var response Action 132 | 133 | data, err := TutumCall(url, request, body) 134 | if err != nil { 135 | return response, err 136 | } 137 | 138 | err = json.Unmarshal(data, &response) 139 | if err != nil { 140 | return response, err 141 | } 142 | 143 | return response, nil 144 | } 145 | 146 | func (self *Action) Retry() (Action, error) { 147 | url := "" 148 | if string(self.Uuid[0]) == "/" { 149 | url = self.Uuid[8:] 150 | } else { 151 | url = "action/" + self.Uuid + "/retry/" 152 | } 153 | 154 | request := "POST" 155 | //Empty Body Request 156 | body := []byte(`{}`) 157 | var response Action 158 | 159 | data, err := TutumCall(url, request, body) 160 | if err != nil { 161 | return response, err 162 | } 163 | 164 | err = json.Unmarshal(data, &response) 165 | if err != nil { 166 | return response, err 167 | } 168 | 169 | return response, nil 170 | } 171 | -------------------------------------------------------------------------------- /tutum/trigger.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListTriggers 7 | Returns : Array of Trigger objects 8 | */ 9 | func (self *Service) ListTriggers() (TriggerListResponse, error) { 10 | url := "service/" + self.Uuid + "/trigger/" 11 | request := "GET" 12 | //Empty Body Request 13 | body := []byte(`{}`) 14 | var response TriggerListResponse 15 | var finalResponse TriggerListResponse 16 | 17 | data, err := TutumCall(url, request, body) 18 | if err != nil { 19 | return response, err 20 | } 21 | 22 | err = json.Unmarshal(data, &response) 23 | if err != nil { 24 | return response, err 25 | } 26 | 27 | finalResponse = response 28 | 29 | Loop: 30 | for { 31 | if response.Meta.Next != "" { 32 | var nextResponse TriggerListResponse 33 | data, err := TutumCall(response.Meta.Next[8:], request, body) 34 | if err != nil { 35 | return nextResponse, err 36 | } 37 | err = json.Unmarshal(data, &nextResponse) 38 | if err != nil { 39 | return nextResponse, err 40 | } 41 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 42 | response = nextResponse 43 | 44 | } else { 45 | break Loop 46 | } 47 | } 48 | 49 | return finalResponse, nil 50 | } 51 | 52 | /* 53 | func GetTrigger 54 | Argument : service uuid and Trigger uuid 55 | Returns : Trigger JSON object 56 | */ 57 | func (self *Service) GetTrigger(trigger_uuid string) (Trigger, error) { 58 | 59 | url := "" 60 | if string(trigger_uuid[0]) == "/" { 61 | url = trigger_uuid[8:] 62 | } else { 63 | url = "service/" + self.Uuid + "/trigger/" + trigger_uuid + "/" 64 | } 65 | 66 | request := "GET" 67 | body := []byte(`{}`) 68 | var response Trigger 69 | 70 | data, err := TutumCall(url, request, body) 71 | if err != nil { 72 | return response, err 73 | } 74 | 75 | err = json.Unmarshal(data, &response) 76 | if err != nil { 77 | return response, err 78 | } 79 | 80 | return response, nil 81 | } 82 | 83 | /* 84 | func CreateTrigger 85 | Argument : service uuid and Trigger JSON object 86 | Returns : Array of Trigger objects 87 | */ 88 | func (self *Service) CreateTrigger(createRequest TriggerCreateRequest) (Trigger, error) { 89 | 90 | url := "service/" + self.Uuid + "/trigger/" 91 | request := "POST" 92 | var response Trigger 93 | 94 | newTrigger, err := json.Marshal(createRequest) 95 | if err != nil { 96 | return response, err 97 | } 98 | 99 | data, err := TutumCall(url, request, newTrigger) 100 | if err != nil { 101 | return response, err 102 | } 103 | 104 | err = json.Unmarshal(data, &response) 105 | if err != nil { 106 | return response, err 107 | } 108 | 109 | return response, nil 110 | } 111 | 112 | /* 113 | func DeleteTrigger 114 | Argument : service uuid and Trigger uuid 115 | */ 116 | func (self *Service) DeleteTrigger(trigger_uuid string) error { 117 | url := "" 118 | if string(trigger_uuid[0]) == "/" { 119 | url = trigger_uuid[8:] 120 | } else { 121 | url = "service/" + self.Uuid + "/trigger/" + trigger_uuid + "/" 122 | } 123 | 124 | request := "DELETE" 125 | body := []byte(`{}`) 126 | var response Trigger 127 | 128 | data, err := TutumCall(url, request, body) 129 | if err != nil { 130 | return err 131 | } 132 | 133 | err = json.Unmarshal(data, &response) 134 | if err != nil { 135 | return err 136 | } 137 | 138 | return nil 139 | } 140 | 141 | /* 142 | func CallTrigger 143 | Argument : service uuid and Trigger uuid 144 | Returns : Trigger JSON object 145 | */ 146 | func (self *Service) CallTrigger(trigger_uuid string) (Trigger, error) { 147 | url := "" 148 | if string(trigger_uuid[0]) == "/" { 149 | url = trigger_uuid[8:] 150 | } else { 151 | url = "service/" + self.Uuid + "/trigger/" + trigger_uuid + "/call/" 152 | } 153 | 154 | request := "POST" 155 | body := []byte(`{}`) 156 | var response Trigger 157 | 158 | data, err := TutumCall(url, request, body) 159 | if err != nil { 160 | return response, err 161 | } 162 | 163 | err = json.Unmarshal(data, &response) 164 | if err != nil { 165 | return response, err 166 | } 167 | return response, nil 168 | } 169 | -------------------------------------------------------------------------------- /tutum/tutum.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "os" 7 | "os/user" 8 | "strings" 9 | 10 | "github.com/BurntSushi/toml" 11 | ) 12 | 13 | var ( 14 | User string 15 | ApiKey string 16 | BasicAuth string 17 | AuthHeader string 18 | BaseUrl = "https://dashboard.tutum.co/api/v1/" 19 | StreamUrl = "wss://stream.tutum.co:443/v1/" 20 | version = "0.21.0" 21 | ) 22 | 23 | type config map[string]Auth 24 | 25 | type Auth struct { 26 | User string 27 | Apikey string 28 | Basic_auth string 29 | } 30 | 31 | type TutumObject interface { 32 | Start() error 33 | Stop() error 34 | Redeploy() error 35 | Terminate() error 36 | Deploy() error 37 | Upgrade() error 38 | Update([]byte) error 39 | } 40 | 41 | func init() { 42 | LoadAuth() 43 | } 44 | 45 | func LoadAuth() error { 46 | if User != "" && ApiKey != "" { 47 | sEnc := base64.StdEncoding.EncodeToString([]byte(User + ":" + ApiKey)) 48 | AuthHeader = fmt.Sprintf("Basic %s", sEnc) 49 | return nil 50 | } else { 51 | if os.Getenv("TUTUM_AUTH") != "" { 52 | AuthHeader = os.Getenv("TUTUM_AUTH") 53 | } 54 | } 55 | 56 | // Process ~/.tutum configuration file first 57 | if usr, err := user.Current(); err == nil { 58 | var conf config 59 | confFilePath := usr.HomeDir + "/.tutum" 60 | if _, err := os.Stat(confFilePath); !os.IsNotExist(err) { 61 | if _, err := toml.DecodeFile(confFilePath, &conf); err == nil { 62 | if conf["auth"].User != "" && conf["auth"].Apikey != "" { 63 | User = conf["auth"].User 64 | ApiKey = conf["auth"].Apikey 65 | sEnc := base64.StdEncoding.EncodeToString([]byte(User + ":" + ApiKey)) 66 | AuthHeader = fmt.Sprintf("Basic %s", sEnc) 67 | return nil 68 | } else { 69 | if conf["auth"].Basic_auth != "" { 70 | BasicAuth = conf["auth"].Basic_auth 71 | AuthHeader = fmt.Sprintf("Basic %s", BasicAuth) 72 | return nil 73 | } 74 | } 75 | } else { 76 | return fmt.Errorf("Malformed Tutum configuration file found at %s: %s", confFilePath, err) 77 | } 78 | } 79 | } 80 | 81 | // Load environment variables as an alternative option 82 | if os.Getenv("TUTUM_USER") != "" && os.Getenv("TUTUM_APIKEY") != "" { 83 | User = os.Getenv("TUTUM_USER") 84 | ApiKey = os.Getenv("TUTUM_APIKEY") 85 | sEnc := base64.StdEncoding.EncodeToString([]byte(User + ":" + ApiKey)) 86 | AuthHeader = fmt.Sprintf("Basic %s", sEnc) 87 | return nil 88 | } 89 | 90 | return fmt.Errorf("Couldn't find any Tutum credentials in ~/.tutum or environment variables TUTUM_USER and TUTUM_APIKEY") 91 | } 92 | 93 | func IsAuthenticated() bool { 94 | return (AuthHeader != "") 95 | } 96 | 97 | func FetchByResourceUri(id string) interface{} { 98 | words := strings.Split(id, "/") 99 | switch words[3] { 100 | case "action": 101 | action, err := GetAction(id) 102 | 103 | if err != nil { 104 | return err 105 | } 106 | 107 | return action 108 | case "nodecluster": 109 | nodecluster, err := GetNodeCluster(id) 110 | 111 | if err != nil { 112 | return err 113 | } 114 | 115 | return nodecluster 116 | case "provider": 117 | provider, err := GetProvider(id) 118 | 119 | if err != nil { 120 | return err 121 | } 122 | 123 | return provider 124 | case "region": 125 | region, err := GetRegion(id) 126 | 127 | if err != nil { 128 | return err 129 | } 130 | 131 | return region 132 | case "service": 133 | service, err := GetService(id) 134 | 135 | if err != nil { 136 | return err 137 | } 138 | 139 | return service 140 | case "stack": 141 | stack, err := GetStack(id) 142 | 143 | if err != nil { 144 | return err 145 | } 146 | 147 | return stack 148 | case "volume": 149 | volume, err := GetVolume(id) 150 | 151 | if err != nil { 152 | return err 153 | } 154 | 155 | return volume 156 | case "volumegroup": 157 | volumegroup, err := GetVolumeGroup(id) 158 | 159 | if err != nil { 160 | return err 161 | } 162 | 163 | return volumegroup 164 | case "node": 165 | node, err := GetNode(id) 166 | 167 | if err != nil { 168 | return err 169 | } 170 | 171 | return node 172 | case "container": 173 | container, err := GetContainer(id) 174 | 175 | if err != nil { 176 | return err 177 | } 178 | 179 | return container 180 | } 181 | return 0 182 | } 183 | -------------------------------------------------------------------------------- /tutum/node_cluster.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import "encoding/json" 4 | 5 | /* 6 | func ListNodeClusters 7 | Returns : Array of NodeCluster objects 8 | */ 9 | func ListNodeClusters() (NodeClusterListResponse, error) { 10 | 11 | url := "nodecluster/" 12 | request := "GET" 13 | 14 | //Empty Body Request 15 | body := []byte(`{}`) 16 | var response NodeClusterListResponse 17 | var finalResponse NodeClusterListResponse 18 | 19 | data, err := TutumCall(url, request, body) 20 | if err != nil { 21 | return response, err 22 | } 23 | 24 | err = json.Unmarshal(data, &response) 25 | if err != nil { 26 | return response, err 27 | } 28 | 29 | finalResponse = response 30 | 31 | Loop: 32 | for { 33 | if response.Meta.Next != "" { 34 | var nextResponse NodeClusterListResponse 35 | data, err := TutumCall(response.Meta.Next[8:], request, body) 36 | if err != nil { 37 | return nextResponse, err 38 | } 39 | err = json.Unmarshal(data, &nextResponse) 40 | if err != nil { 41 | return nextResponse, err 42 | } 43 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 44 | response = nextResponse 45 | 46 | } else { 47 | break Loop 48 | } 49 | } 50 | 51 | return finalResponse, nil 52 | } 53 | 54 | /* 55 | func GetNodeCluster 56 | Argument : uuid 57 | Returns : NodeCluster JSON object 58 | */ 59 | func GetNodeCluster(uuid string) (NodeCluster, error) { 60 | 61 | url := "" 62 | if string(uuid[0]) == "/" { 63 | url = uuid[8:] + "/" 64 | } else { 65 | url = "nodecluster/" + uuid + "/" 66 | } 67 | 68 | request := "GET" 69 | //Empty Body Request 70 | body := []byte(`{}`) 71 | var response NodeCluster 72 | 73 | data, err := TutumCall(url, request, body) 74 | if err != nil { 75 | return response, err 76 | } 77 | 78 | err = json.Unmarshal(data, &response) 79 | if err != nil { 80 | return response, err 81 | } 82 | 83 | return response, nil 84 | } 85 | 86 | /* 87 | func CreateNodeCluster 88 | Argument : NodeCluster JSON object (see documentation) 89 | Returns : NodeCluster JSON object 90 | */ 91 | func CreateNodeCluster(createRequest NodeCreateRequest) (NodeCluster, error) { 92 | 93 | url := "nodecluster/" 94 | request := "POST" 95 | var response NodeCluster 96 | 97 | newCluster, err := json.Marshal(createRequest) 98 | if err != nil { 99 | return response, err 100 | } 101 | 102 | data, err := TutumCall(url, request, newCluster) 103 | if err != nil { 104 | return response, err 105 | } 106 | 107 | err = json.Unmarshal(data, &response) 108 | if err != nil { 109 | return response, err 110 | } 111 | 112 | return response, nil 113 | } 114 | 115 | /* 116 | func DeployNodeCluster 117 | Argument : uuid 118 | Returns : NodeCluster JSON object 119 | */ 120 | func (self *NodeCluster) Deploy() error { 121 | 122 | url := "nodecluster/" + self.Uuid + "/deploy/" 123 | request := "POST" 124 | //Empty Body Request 125 | body := []byte(`{}`) 126 | 127 | _, err := TutumCall(url, request, body) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | return nil 133 | } 134 | 135 | /* 136 | func UpdateNodeCluster 137 | Argument : uuid and nodecluster JSON object (see documentation) 138 | Returns : NodeCluster JSON object 139 | */ 140 | func (self *NodeCluster) Update(createRequest NodeCreateRequest) error { 141 | 142 | url := "nodecluster/" + self.Uuid + "/" 143 | request := "PATCH" 144 | 145 | updatedNodeCluster, err := json.Marshal(createRequest) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | _, errr := TutumCall(url, request, updatedNodeCluster) 151 | if errr != nil { 152 | return errr 153 | } 154 | 155 | return nil 156 | } 157 | 158 | /* 159 | func UpgradeNodeCluster 160 | Argument : uuid 161 | Returns : NodeCluster JSON object 162 | */ 163 | func (self *NodeCluster) Upgrade() error { 164 | 165 | url := "nodecluster/" + self.Uuid + "/docker-upgrade/" 166 | request := "POST" 167 | //Empty Body Request 168 | body := []byte(`{}`) 169 | 170 | _, err := TutumCall(url, request, body) 171 | if err != nil { 172 | return err 173 | } 174 | 175 | return nil 176 | } 177 | 178 | /* 179 | func TerminateNodeCluster 180 | Argument : uuid 181 | Returns : NodeCluster JSON object 182 | */ 183 | func (self *NodeCluster) Terminate() error { 184 | 185 | url := "nodecluster/" + self.Uuid + "/" 186 | request := "DELETE" 187 | //Empty Body Request 188 | body := []byte(`{}`) 189 | 190 | _, err := TutumCall(url, request, body) 191 | if err != nil { 192 | return err 193 | } 194 | 195 | return nil 196 | } 197 | -------------------------------------------------------------------------------- /tutum/stack.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | ) 7 | 8 | /* 9 | func ListStacks 10 | Returns : Array of Stack objects 11 | */ 12 | func ListStacks() (StackListResponse, error) { 13 | url := "stack/" 14 | request := "GET" 15 | 16 | //Empty Body Request 17 | body := []byte(`{}`) 18 | var response StackListResponse 19 | var finalResponse StackListResponse 20 | 21 | data, err := TutumCall(url, request, body) 22 | if err != nil { 23 | return response, err 24 | } 25 | 26 | err = json.Unmarshal(data, &response) 27 | if err != nil { 28 | return response, err 29 | } 30 | 31 | finalResponse = response 32 | 33 | Loop: 34 | for { 35 | if response.Meta.Next != "" { 36 | var nextResponse StackListResponse 37 | data, err := TutumCall(response.Meta.Next[8:], request, body) 38 | if err != nil { 39 | return nextResponse, err 40 | } 41 | err = json.Unmarshal(data, &nextResponse) 42 | if err != nil { 43 | return nextResponse, err 44 | } 45 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 46 | response = nextResponse 47 | 48 | } else { 49 | break Loop 50 | } 51 | } 52 | 53 | return finalResponse, nil 54 | } 55 | 56 | /* 57 | func GetStack 58 | Argument : uuid 59 | Returns : Stack JSON object 60 | */ 61 | func GetStack(uuid string) (Stack, error) { 62 | 63 | url := "" 64 | if string(uuid[0]) == "/" { 65 | url = uuid[8:] 66 | } else { 67 | url = "stack/" + uuid + "/" 68 | } 69 | 70 | request := "GET" 71 | //Empty Body Request 72 | body := []byte(`{}`) 73 | var response Stack 74 | 75 | data, err := TutumCall(url, request, body) 76 | if err != nil { 77 | return response, err 78 | } 79 | 80 | err = json.Unmarshal(data, &response) 81 | if err != nil { 82 | return response, err 83 | } 84 | 85 | return response, nil 86 | } 87 | 88 | /* 89 | func Export 90 | Returns : String that contains the Stack details 91 | */ 92 | func (self *Stack) ExportStack() (string, error) { 93 | 94 | url := "stack/" + self.Uuid + "/export/" 95 | request := "GET" 96 | //Empty Body Request 97 | body := []byte(`{}`) 98 | 99 | data, err := TutumCall(url, request, body) 100 | if err != nil { 101 | return "", err 102 | } 103 | 104 | s := string(data) 105 | 106 | return s, nil 107 | } 108 | 109 | /* 110 | func CreateStack 111 | Argument : Stack JSON object (see documentation) 112 | */ 113 | func CreateStack(createRequest StackCreateRequest) (Stack, error) { 114 | url := "stack/" 115 | request := "POST" 116 | var response Stack 117 | 118 | newStack, err := json.Marshal(createRequest) 119 | if err != nil { 120 | return response, err 121 | } 122 | 123 | log.Println(string(newStack)) 124 | 125 | data, err := TutumCall(url, request, newStack) 126 | if err != nil { 127 | return response, err 128 | } 129 | 130 | err = json.Unmarshal(data, &response) 131 | if err != nil { 132 | return response, err 133 | } 134 | 135 | return response, nil 136 | } 137 | 138 | /* 139 | func Update 140 | Argument : a Stack JSON object (see documentation) 141 | */ 142 | func (self *Stack) Update(createRequest StackCreateRequest) error { 143 | 144 | url := "stack/" + self.Uuid + "/" 145 | request := "PATCH" 146 | 147 | updatedStack, err := json.Marshal(createRequest) 148 | if err != nil { 149 | return err 150 | } 151 | 152 | log.Println(string(updatedStack)) 153 | 154 | _, errr := TutumCall(url, request, updatedStack) 155 | if errr != nil { 156 | return errr 157 | } 158 | 159 | return nil 160 | } 161 | 162 | func (self *Stack) Start() error { 163 | 164 | url := "stack/" + self.Uuid + "/start/" 165 | request := "POST" 166 | //Empty Body Request 167 | body := []byte(`{}`) 168 | 169 | _, err := TutumCall(url, request, body) 170 | if err != nil { 171 | return err 172 | } 173 | 174 | return nil 175 | } 176 | 177 | func (self *Stack) Stop() error { 178 | 179 | url := "stack/" + self.Uuid + "/stop/" 180 | request := "POST" 181 | //Empty Body Request 182 | body := []byte(`{}`) 183 | 184 | _, err := TutumCall(url, request, body) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | return nil 190 | } 191 | 192 | func (self *Stack) Redeploy(reuse_volume ReuseVolumesOption) error { 193 | 194 | url := "" 195 | if reuse_volume.Reuse != true { 196 | url = "stack/" + self.Uuid + "/redeploy/?reuse_volumes=false" 197 | } else { 198 | url = "stack/" + self.Uuid + "/redeploy/" 199 | } 200 | 201 | log.Println(url) 202 | request := "POST" 203 | //Empty Body Request 204 | body := []byte(`{}`) 205 | 206 | _, err := TutumCall(url, request, body) 207 | if err != nil { 208 | return err 209 | } 210 | 211 | return nil 212 | } 213 | 214 | func (self *Stack) Terminate() error { 215 | 216 | url := "stack/" + self.Uuid + "/" 217 | request := "DELETE" 218 | //Empty Body Request 219 | body := []byte(`{}`) 220 | 221 | _, err := TutumCall(url, request, body) 222 | if err != nil { 223 | return err 224 | } 225 | 226 | return nil 227 | } 228 | -------------------------------------------------------------------------------- /tutum/container.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | 10 | "github.com/gorilla/websocket" 11 | ) 12 | 13 | /* 14 | func ListContainers 15 | Returns : Array of Container objects 16 | */ 17 | func ListContainers() (CListResponse, error) { 18 | 19 | url := "container/" 20 | request := "GET" 21 | //Empty Body Request 22 | body := []byte(`{}`) 23 | var response CListResponse 24 | var finalResponse CListResponse 25 | 26 | data, err := TutumCall(url, request, body) 27 | if err != nil { 28 | return response, err 29 | } 30 | 31 | err = json.Unmarshal(data, &response) 32 | if err != nil { 33 | return response, err 34 | } 35 | 36 | finalResponse = response 37 | 38 | Loop: 39 | for { 40 | if response.Meta.Next != "" { 41 | var nextResponse CListResponse 42 | data, err := TutumCall(response.Meta.Next[8:], request, body) 43 | if err != nil { 44 | return nextResponse, err 45 | } 46 | err = json.Unmarshal(data, &nextResponse) 47 | if err != nil { 48 | return nextResponse, err 49 | } 50 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 51 | response = nextResponse 52 | 53 | } else { 54 | break Loop 55 | } 56 | } 57 | 58 | return finalResponse, nil 59 | } 60 | 61 | /* 62 | func GetContainer 63 | Argument : uuid 64 | Returns : Container JSON object 65 | */ 66 | func GetContainer(uuid string) (Container, error) { 67 | 68 | url := "" 69 | if string(uuid[0]) == "/" { 70 | url = uuid[8:] 71 | } else { 72 | url = "container/" + uuid + "/" 73 | } 74 | 75 | request := "GET" 76 | //Empty Body Request 77 | body := []byte(`{}`) 78 | var response Container 79 | 80 | data, err := TutumCall(url, request, body) 81 | if err != nil { 82 | return response, err 83 | } 84 | 85 | err = json.Unmarshal(data, &response) 86 | if err != nil { 87 | return response, err 88 | } 89 | 90 | return response, nil 91 | } 92 | 93 | /* 94 | func GetContainerLogs 95 | Argument : a channel of type string for the output 96 | */ 97 | 98 | func (self *Container) Logs(c chan Logs) { 99 | 100 | endpoint := "container/" + self.Uuid + "/logs/?user=" + User + "&token=" + ApiKey 101 | url := StreamUrl + endpoint 102 | 103 | header := http.Header{} 104 | header.Add("User-Agent", customUserAgent) 105 | 106 | var Dialer websocket.Dialer 107 | ws, _, err := Dialer.Dial(url, header) 108 | if err != nil { 109 | log.Println(err) 110 | } 111 | 112 | var msg Logs 113 | for { 114 | if err = ws.ReadJSON(&msg); err != nil { 115 | if err != nil && err.Error() != "EOF" { 116 | log.Println(err) 117 | } else { 118 | break 119 | } 120 | } 121 | c <- msg 122 | } 123 | } 124 | 125 | /* 126 | func Exec 127 | Arguments : the command to execute, a channel of type string for the output 128 | */ 129 | 130 | func (self *Container) Exec(command string, c chan Exec) { 131 | go self.Run(command, c) 132 | Loop: 133 | for { 134 | select { 135 | case s := <-c: 136 | if s.Output != "EOF" { 137 | fmt.Printf("%s", s.Output) 138 | } else { 139 | break Loop 140 | } 141 | } 142 | } 143 | } 144 | 145 | func (self *Container) Run(command string, c chan Exec) { 146 | 147 | endpoint := "container/" + self.Uuid + "/exec/?user=" + User + "&token=" + ApiKey + "&command=" + url.QueryEscape(command) 148 | url := StreamUrl + endpoint 149 | 150 | header := http.Header{} 151 | header.Add("User-Agent", customUserAgent) 152 | 153 | var Dialer websocket.Dialer 154 | ws, _, err := Dialer.Dial(url, header) 155 | if err != nil { 156 | log.Println(err) 157 | } 158 | 159 | var msg Exec 160 | for { 161 | if err = ws.ReadJSON(&msg); err != nil { 162 | if err != nil && err.Error() != "EOF" { 163 | log.Println(err) 164 | } else { 165 | break 166 | } 167 | } 168 | c <- msg 169 | } 170 | } 171 | 172 | /* 173 | func StartContainer 174 | Returns : Error 175 | */ 176 | func (self *Container) Start() error { 177 | 178 | url := "container/" + self.Uuid + "/start/" 179 | request := "POST" 180 | //Empty Body Request 181 | body := []byte(`{}`) 182 | var response Container 183 | 184 | data, err := TutumCall(url, request, body) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | err = json.Unmarshal(data, &response) 190 | if err != nil { 191 | return err 192 | } 193 | 194 | return nil 195 | } 196 | 197 | /* 198 | func StopContainer 199 | Returns : Error 200 | */ 201 | func (self *Container) Stop() error { 202 | 203 | url := "container/" + self.Uuid + "/stop/" 204 | request := "POST" 205 | //Empty Body Request 206 | body := []byte(`{}`) 207 | 208 | _, err := TutumCall(url, request, body) 209 | if err != nil { 210 | return err 211 | } 212 | 213 | return nil 214 | } 215 | 216 | /* 217 | func RedeployContainer 218 | Returns : Error 219 | */ 220 | func (self *Container) Redeploy(reuse_volume ReuseVolumesOption) error { 221 | 222 | url := "" 223 | if reuse_volume.Reuse != true { 224 | url = "container/" + self.Uuid + "/redeploy/?reuse_volumes=false" 225 | } else { 226 | url = "container/" + self.Uuid + "/redeploy/" 227 | } 228 | 229 | request := "POST" 230 | //Empty Body Request 231 | body := []byte(`{}`) 232 | 233 | _, err := TutumCall(url, request, body) 234 | if err != nil { 235 | return err 236 | } 237 | 238 | return nil 239 | } 240 | 241 | /* 242 | func TerminateContainer 243 | Returns : Error 244 | */ 245 | func (self *Container) Terminate() error { 246 | 247 | url := "container/" + self.Uuid + "/" 248 | request := "DELETE" 249 | //Empty Body Request 250 | body := []byte(`{}`) 251 | 252 | _, err := TutumCall(url, request, body) 253 | if err != nil { 254 | return err 255 | } 256 | 257 | return nil 258 | } 259 | -------------------------------------------------------------------------------- /tutum/service.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | /* 12 | func ListServices 13 | Returns : Array of Service objects 14 | */ 15 | func ListServices() (SListResponse, error) { 16 | url := "service/" 17 | request := "GET" 18 | //Empty Body Request 19 | body := []byte(`{}`) 20 | var response SListResponse 21 | var finalResponse SListResponse 22 | 23 | data, err := TutumCall(url, request, body) 24 | if err != nil { 25 | return response, err 26 | } 27 | 28 | err = json.Unmarshal(data, &response) 29 | if err != nil { 30 | return response, err 31 | } 32 | 33 | finalResponse = response 34 | 35 | Loop: 36 | for { 37 | if response.Meta.Next != "" { 38 | var nextResponse SListResponse 39 | data, err := TutumCall(response.Meta.Next[8:], request, body) 40 | if err != nil { 41 | return nextResponse, err 42 | } 43 | err = json.Unmarshal(data, &nextResponse) 44 | if err != nil { 45 | return nextResponse, err 46 | } 47 | finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...) 48 | response = nextResponse 49 | 50 | } else { 51 | break Loop 52 | } 53 | 54 | } 55 | 56 | return finalResponse, nil 57 | 58 | } 59 | 60 | /* 61 | func GetService 62 | Argument : uuid 63 | Returns : Service JSON object 64 | */ 65 | func GetService(uuid string) (Service, error) { 66 | 67 | url := "" 68 | if string(uuid[0]) == "/" { 69 | url = uuid[8:] 70 | } else { 71 | url = "service/" + uuid + "/" 72 | } 73 | 74 | request := "GET" 75 | //Empty Body Request 76 | body := []byte(`{}`) 77 | var response Service 78 | 79 | data, err := TutumCall(url, request, body) 80 | if err != nil { 81 | return response, err 82 | } 83 | 84 | err = json.Unmarshal(data, &response) 85 | if err != nil { 86 | return response, err 87 | } 88 | 89 | return response, nil 90 | 91 | } 92 | 93 | /* 94 | func CreateService 95 | Argument : Service JSON object (see documentation) 96 | Returns : Service JSON object 97 | */ 98 | func CreateService(createRequest ServiceCreateRequest) (Service, error) { 99 | 100 | url := "service/" 101 | request := "POST" 102 | var response Service 103 | 104 | newService, err := json.Marshal(createRequest) 105 | if err != nil { 106 | return response, err 107 | } 108 | 109 | data, err := TutumCall(url, request, newService) 110 | if err != nil { 111 | return response, err 112 | } 113 | 114 | err = json.Unmarshal(data, &response) 115 | if err != nil { 116 | return response, err 117 | } 118 | 119 | return response, nil 120 | } 121 | 122 | /* 123 | func Logs 124 | Argument : a channel of type string for the output 125 | */ 126 | 127 | func (self *Service) Logs(c chan Logs) { 128 | 129 | endpoint := "service/" + self.Uuid + "/logs/?user=" + User + "&token=" + ApiKey 130 | url := StreamUrl + endpoint 131 | 132 | header := http.Header{} 133 | header.Add("User-Agent", customUserAgent) 134 | 135 | var Dialer websocket.Dialer 136 | ws, _, err := Dialer.Dial(url, header) 137 | if err != nil { 138 | log.Println(err) 139 | } 140 | 141 | var msg Logs 142 | for { 143 | if err = ws.ReadJSON(&msg); err != nil { 144 | if err != nil && err.Error() != "EOF" { 145 | log.Println(err) 146 | } else { 147 | break 148 | } 149 | } 150 | c <- msg 151 | } 152 | } 153 | 154 | /* 155 | func UpdateService 156 | Argument : updatedService JSON object 157 | Returns : Error 158 | */ 159 | func (self *Service) Scale() error { 160 | 161 | url := "service/" + self.Uuid + "/scale/" 162 | request := "POST" 163 | //Empty Body Request 164 | body := []byte(`{}`) 165 | var response Service 166 | 167 | data, err := TutumCall(url, request, body) 168 | if err != nil { 169 | return err 170 | } 171 | 172 | err = json.Unmarshal(data, &response) 173 | if err != nil { 174 | return err 175 | } 176 | 177 | return nil 178 | } 179 | 180 | /* 181 | func UpdateService 182 | Argument : updatedService JSON object 183 | Returns : Error 184 | */ 185 | func (self *Service) Update(createRequest ServiceCreateRequest) error { 186 | 187 | url := "service/" + self.Uuid + "/" 188 | request := "PATCH" 189 | 190 | updatedService, err := json.Marshal(createRequest) 191 | if err != nil { 192 | return err 193 | } 194 | 195 | _, err = TutumCall(url, request, updatedService) 196 | if err != nil { 197 | return err 198 | } 199 | 200 | return nil 201 | } 202 | 203 | /* 204 | func StartService 205 | Returns : Error 206 | */ 207 | func (self *Service) Start() error { 208 | url := "service/" + self.Uuid + "/start/" 209 | request := "POST" 210 | //Empty Body Request 211 | body := []byte(`{}`) 212 | _, err := TutumCall(url, request, body) 213 | if err != nil { 214 | return err 215 | } 216 | 217 | return nil 218 | } 219 | 220 | /* 221 | func StopService 222 | Returns : Error 223 | */ 224 | func (self *Service) StopService() error { 225 | 226 | url := "service/" + self.Uuid + "/stop/" 227 | request := "POST" 228 | //Empty Body Request 229 | body := []byte(`{}`) 230 | _, err := TutumCall(url, request, body) 231 | if err != nil { 232 | return err 233 | } 234 | 235 | return nil 236 | } 237 | 238 | /* 239 | func RedeployService 240 | Returns : Error 241 | */ 242 | func (self *Service) Redeploy(reuse_volume ReuseVolumesOption) error { 243 | 244 | url := "" 245 | if reuse_volume.Reuse != true { 246 | url = "service/" + self.Uuid + "/redeploy/?reuse_volumes=false" 247 | } else { 248 | url = "service/" + self.Uuid + "/redeploy/" 249 | } 250 | 251 | request := "POST" 252 | //Empty Body Request 253 | body := []byte(`{}`) 254 | 255 | _, err := TutumCall(url, request, body) 256 | if err != nil { 257 | return err 258 | } 259 | 260 | return nil 261 | } 262 | 263 | /* 264 | func TerminateService 265 | Returns : Error 266 | */ 267 | func (self *Service) TerminateService() error { 268 | url := "service/" + self.Uuid + "/" 269 | request := "DELETE" 270 | //Empty Body Request 271 | body := []byte(`{}`) 272 | 273 | _, err := TutumCall(url, request, body) 274 | if err != nil { 275 | return err 276 | } 277 | 278 | return nil 279 | } 280 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2015 Tutum, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tutum/type.go: -------------------------------------------------------------------------------- 1 | package tutum 2 | 3 | type ActionListResponse struct { 4 | Meta Meta `json:"meta"` 5 | Objects []Action `json:"objects"` 6 | } 7 | 8 | type Action struct { 9 | Action string `json:"action"` 10 | Body string `json:"body"` 11 | End_date string `json:"end_date"` 12 | Ip string `json:"ip"` 13 | Location string `json:"location"` 14 | Logs string `json:"logs"` 15 | Method string `json:"method"` 16 | Object string `json:"object"` 17 | Path string `json:"path"` 18 | Resource_uri string `json:"resource_uri"` 19 | Start_date string `json:"start_date"` 20 | State string `json:"state"` 21 | Uuid string `json:"uuid"` 22 | } 23 | 24 | type AZListResponse struct { 25 | Meta Meta `json:"meta"` 26 | Objects []AZ `json:"objects"` 27 | } 28 | 29 | type AZ struct { 30 | Available bool `json:"available"` 31 | Name string `json:"name"` 32 | Resource_uri string `json:"resource_uri"` 33 | } 34 | 35 | type ContainerBinding struct { 36 | Container_path string `json:"container_path"` 37 | Host_path string `json:"host_path"` 38 | Rewritable bool `json:"rewritable"` 39 | Volume string `json:"volume"` 40 | } 41 | 42 | type Meta struct { 43 | Limit int `json:"limit"` 44 | Next string `json:"next"` 45 | TotalCount int `json:"total_count"` 46 | } 47 | 48 | type Metric struct { 49 | Cpu float64 `json:"cpu"` 50 | Disk float64 `json:"disk"` 51 | Memory float64 `json:"memory"` 52 | } 53 | 54 | type CListResponse struct { 55 | Meta Meta `json:"meta"` 56 | Objects []Container `json:"objects"` 57 | } 58 | 59 | type Container struct { 60 | Autodestroy string `json:"autodestroy"` 61 | Autorestart string `json:"autorestart"` 62 | Bindings []ContainerBinding `json:"bindings"` 63 | Container_envvars []ContainerEnvvar `json:"container_envvars"` 64 | Container_ports []ContainerPortInfo `json:"container_ports"` 65 | Cpu_shares int `json:"cpu_shares"` 66 | Deployed_datetime string `json:"deployed_datetime"` 67 | Destroyed_datetime string `json:"destroyed_datetime"` 68 | Entrypoint string `json:"entrypoint"` 69 | Exit_code int `json:"exit_code"` 70 | Exit_code_message string `json:"exit_code_message"` 71 | Image_name string `json:"image_name"` 72 | Image_tag string `json:"image_tag"` 73 | Last_metric Metric `json:"last_metric"` 74 | Link_variables map[string]string `json:"link_variables"` 75 | Linked_to_container []ContainerLinkInfo `json:"linked_to_container"` 76 | Memory int `json:"memory"` 77 | Name string `json:"name"` 78 | Net string `json:"net"` 79 | Node string `json:"node"` 80 | Pid string `json:"pid"` 81 | Private_ip string `json:"private_ip"` 82 | Privileged bool `json:"privileged"` 83 | Public_dns string `json:"public_dns"` 84 | Resource_uri string `json:"resource_uri"` 85 | Roles []string `json:"roles"` 86 | Run_command string `json:"run_command"` 87 | Service string `json:"service"` 88 | Started_datetime string `json:"started_datetime"` 89 | State string `json:"state"` 90 | Stopped_datetime string `json:"stopped_datetime"` 91 | Synchronized bool `json:"synchronized"` 92 | Uuid string `json:"uuid"` 93 | Working_dir string `json:"working_dir"` 94 | } 95 | 96 | type ContainerEnvvar struct { 97 | Key string `json:"key"` 98 | Value string `json:"value"` 99 | } 100 | 101 | type ContainerLinkInfo struct { 102 | Endpoints map[string]string `json:"endpoints"` 103 | From_container string `json:"from_container"` 104 | Name string `json:"name"` 105 | To_container string `json:"to_container"` 106 | } 107 | 108 | type ContainerPortInfo struct { 109 | Container string `json:"container"` 110 | Inner_port int `json:"inner_port"` 111 | Outer_port int `json:"outer_port"` 112 | Protocol string `json:"protocol"` 113 | } 114 | 115 | type Event struct { 116 | Type string `json:"type"` 117 | Action string `json:"action"` 118 | Parents []string `json:"parents"` 119 | Resource_uri string `json:"resource_uri"` 120 | State string `json:"state"` 121 | } 122 | 123 | type Exec struct { 124 | Type string `json:"type"` 125 | Output string `json:"output"` 126 | StreamType string `json:"streamType"` 127 | } 128 | 129 | type BuildSettings struct { 130 | Autobuild bool `json:"autobuild,omitempty"` 131 | Branch string `json:"branch"` 132 | Dockerfile string `json:"dockerfile"` 133 | Image string `json:"image"` 134 | Resource_uri string `json:"resource_uri"` 135 | State string `json:"state"` 136 | Tag string `json:"tag"` 137 | } 138 | 139 | type BuildSource struct { 140 | Autotest string `json:"autotest,omitempty"` 141 | Build_Settings []string `json:"build_settings,omitempty"` 142 | Owner string `json:"owner,omitempty"` 143 | Repository string `json:"repository,omitempty"` 144 | Type string `json:"type,omitempty"` 145 | } 146 | 147 | type ImageListResponse struct { 148 | Meta Meta `json:"meta"` 149 | Objects []ImageShort `json:"objects"` 150 | } 151 | 152 | type ImageShort struct { 153 | Build_Source string `json:"build_source"` 154 | Description string `json:"description"` 155 | Icon_url string `json:"icon_url"` 156 | In_use bool `json:"in_use"` 157 | Is_private_image bool `json:"is_private_image"` 158 | Jumpstart bool `json:"jumpstart"` 159 | Last_build_date string `json:"last_build_date"` 160 | Name string `json:"name"` 161 | Public_url string `json:"public_url"` 162 | Registry string `json:"registry"` 163 | Resource_uri string `json:"resource_uri"` 164 | Star_count int `json:"star_count"` 165 | State string `json:"state"` 166 | Tags []string `json:"tags"` 167 | } 168 | 169 | type Image struct { 170 | Build_Source BuildSource `json:"build_source"` 171 | Description string `json:"description"` 172 | Icon_url string `json:"icon_url"` 173 | In_use bool `json:"in_use"` 174 | Is_private_image bool `json:"is_private_image"` 175 | Jumpstart bool `json:"jumpstart"` 176 | Last_build_date string `json:"last_build_date"` 177 | Name string `json:"name"` 178 | Public_url string `json:"public_url"` 179 | Registry string `json:"registry"` 180 | Resource_uri string `json:"resource_uri"` 181 | Star_count int `json:"star_count"` 182 | State string `json:"state"` 183 | Tags []string `json:"tags"` 184 | } 185 | 186 | type ImageCreateRequest struct { 187 | BuildSource *BuildSource `json:"build_source,omitempty"` 188 | Name string `json:"name,omitempty"` 189 | Username string `json:"username,omitempty"` 190 | Password string `json:"password,omitempty"` 191 | Description string `json:"description,omitempty"` 192 | } 193 | 194 | type ImageTagsListResponse struct { 195 | Meta Meta `json:"meta"` 196 | Objects []ImageTags `json:"objects"` 197 | } 198 | 199 | type ImageTags struct { 200 | Buildable bool `json:"buildable"` 201 | Full_name string `json:"full_name"` 202 | Layer LayerStruct `json:"layer"` 203 | Image string `json:"image"` 204 | Name string `json:"name"` 205 | Resource_uri string `json:"resource_uri"` 206 | } 207 | 208 | type LayerStruct struct { 209 | Author string `json:"author"` 210 | Creation string `json:"creation"` 211 | Docker_id string `json:"docker_id"` 212 | Entrypoint string `json:"entrypoint"` 213 | Envvars []ContainerEnvvar `json:"envvars"` 214 | Ports []Port `json:"ports"` 215 | Resource_uri string `json:"resource_uri"` 216 | Run_command string `json:"run_command"` 217 | Volumes []VolumePath `json:"volumes"` 218 | } 219 | 220 | type Logs struct { 221 | Type string `json:"type"` 222 | Log string `json:"log"` 223 | StreamType string `json:streamType` 224 | Timestamp int `json:"timestamp"` 225 | } 226 | 227 | type Network struct { 228 | Name string `json:"name"` 229 | CIDR string `json:"cidr"` 230 | } 231 | 232 | type NodeListResponse struct { 233 | Meta Meta `json:"meta"` 234 | Objects []Node `json:"objects"` 235 | } 236 | 237 | type Node struct { 238 | Availability_zone string `json:"availability_zone,omniempty"` 239 | Deployed_datetime string `json:"deployed_datetime,omitempty"` 240 | Destroyed_datetime string `json:"destroyed_datetime,omitempty"` 241 | Docker_version string `json:"docker_version,omitempty"` 242 | Last_seen string `json:"last_seen,omitempty"` 243 | Node_cluster string `json:"node_cluster,omitempty"` 244 | Public_ip string `json:"public_ip,omitempty"` 245 | Private_ips []Network `json:"private_ips,omitempty"` 246 | Region string `json:"region,omitempty"` 247 | Resource_uri string `json:"resource_uri,omitempty"` 248 | State string `json:"state,omitempty"` 249 | Tags []NodeTag `json:"tags,omitempty"` 250 | Uuid string `json:"uuid,omitempty"` 251 | } 252 | 253 | type NodeEvent struct { 254 | Type string `json:"type"` 255 | Log string `json:log` 256 | } 257 | 258 | type NodeTag struct { 259 | Name string `json:"name"` 260 | } 261 | 262 | type NodeClusterListResponse struct { 263 | Meta Meta `json:"meta"` 264 | Objects []NodeCluster `json:"objects"` 265 | } 266 | 267 | type VPC struct { 268 | Id string `json:"id"` 269 | Subnets []string `json:"subnets,omitempty"` 270 | Security_groups []string `json:"security_groups,omitempty"` 271 | } 272 | 273 | type IAM struct { 274 | Instance_profile_name string `json:"instance_profile_name,omitempty"` 275 | } 276 | 277 | type ProviderOption struct { 278 | Vpc VPC `json:"vpc,omitempty"` 279 | Iam IAM `json:"iam,omitempty"` 280 | } 281 | 282 | type NodeCluster struct { 283 | Current_num_nodes int `json:"current_num_nodes"` 284 | Deployed_datetime string `json:"deployed_datetime"` 285 | Destroyed_datetime string `json:"destroyed_datetime"` 286 | Disk int `json:"disk"` 287 | Name string `json:"name"` 288 | Nodes []string `json:"nodes"` 289 | NodeType string `json:"node_type"` 290 | Provider_options ProviderOption `json:"provider_options"` 291 | Region string `json:"region"` 292 | Resource_uri string `json:"resource_uri"` 293 | State string `json:"state"` 294 | Tags []NodeTag `json:"tags,omitempty"` 295 | Target_num_nodes int `json:"target_num_nodes"` 296 | Uuid string `json:"uuid"` 297 | } 298 | 299 | type NodeCreateRequest struct { 300 | Disk int `json:"disk,omitempty"` 301 | Name string `json:"name,omitempty"` 302 | NodeType string `json:"node_type,omitempty"` 303 | Region string `json:"region,omitempty"` 304 | Target_num_nodes int `json:"target_num_nodes,omitempty"` 305 | Tags []NodeTag `json:"tags,omitempty"` 306 | } 307 | 308 | type NodeTypeListResponse struct { 309 | Meta Meta `json:"meta"` 310 | Objects []NodeType `json:"objects"` 311 | } 312 | 313 | type NodeType struct { 314 | Available bool `json:"available"` 315 | Label string `json:"label"` 316 | Name string `json:"name"` 317 | Provider string `json:"provider"` 318 | Regions []string `json:"regions"` 319 | Resource_uri string `json:"resource_uri"` 320 | } 321 | 322 | type Port struct { 323 | Port int `json:"port"` 324 | Protocol string `json:"protocol"` 325 | } 326 | 327 | type ProviderListResponse struct { 328 | Meta Meta `json:"meta"` 329 | Objects []Provider `json:"objects"` 330 | } 331 | 332 | type Provider struct { 333 | Available bool `json:"available"` 334 | Label string `json:"label"` 335 | Name string `json:"name"` 336 | Regions []string `json:"regions"` 337 | Resource_uri string `json:"resource_uri"` 338 | } 339 | 340 | type RegionListResponse struct { 341 | Meta Meta `json:"meta"` 342 | Objects []Region `json:"objects"` 343 | } 344 | 345 | type Region struct { 346 | Available bool `json:"available"` 347 | Label string `json:"label"` 348 | Name string `json:"name"` 349 | Node_types []string `json:"node_types"` 350 | Provider string `json:"provider"` 351 | Resource_uri string `json:"resource_uri"` 352 | } 353 | 354 | type RegistryListResponse struct { 355 | Meta Meta `json:"meta"` 356 | Objects []Registry `json:"objects"` 357 | } 358 | 359 | type Registry struct { 360 | Host string `json:"host"` 361 | Icon_url string `json:"icon_url"` 362 | Is_ssl bool `json:"is_ssl"` 363 | Is_tutum_registry bool `json:"is_tutum_registry"` 364 | Name string `json:"name"` 365 | Resource_uri string `json:"resource_uri"` 366 | } 367 | 368 | type ReuseVolumesOption struct { 369 | Reuse bool 370 | } 371 | 372 | type SListResponse struct { 373 | Meta Meta `json:"meta"` 374 | Objects []Service `json: "objects"` 375 | } 376 | 377 | type Service struct { 378 | Autodestroy string `json:"autodestroy"` 379 | Autoredeploy bool `json:"autoredeploy"` 380 | Autorestart string `json:"autorestart"` 381 | Bindings []ServiceBinding `json:"bindings"` 382 | Container_envvars []ContainerEnvvar `json:"container_envvars"` 383 | Container_ports []ContainerPortInfo `json:"container_ports"` 384 | Containers []string `json:"containers"` 385 | Cpu_shares int `json:"cpu_shares"` 386 | Current_num_containers int `json:"current_num_containers"` 387 | Deployed_datetime string `json:"deployed_datetime"` 388 | Deployment_strategy string `json:"deployment_strategy"` 389 | Destroyed_datetime string `json:"destroyed_datetime"` 390 | Entrypoint string `json:"entrypoint"` 391 | Image_name string `json:"image_name"` 392 | Image_tag string `json:"image_tag"` 393 | Link_variables map[string]string `json:"link_variables"` 394 | Linked_from_service []ServiceLinkInfo `json:"linked_from_service"` 395 | Linked_to_service []ServiceLinkInfo `json:"linked_to_service"` 396 | Memory int `json:"memory"` 397 | Name string `json:"name"` 398 | Net string `json:"net"` 399 | Pid string `json:"pid"` 400 | Privileged bool `json:"privileged"` 401 | Public_dns string `json:"public_dns"` 402 | Resource_uri string `json:"resource_uri"` 403 | Roles []string `json:"roles"` 404 | Run_command string `json:"run_command"` 405 | Running_num_containers int `json:"running_num_containers"` 406 | Sequential_deployment bool `json:"sequential_deployment"` 407 | Stack string `json:"stack"` 408 | Started_datetime string `json:"started_datetime"` 409 | State string `json:"state"` 410 | Stopped_datetime string `json:"stopped_datetime"` 411 | Stopped_num_containers int `json:"stopped_num_containers"` 412 | Synchronized bool `json:"synchronized"` 413 | Tags []ServiceTag `json:"tags"` 414 | Target_num_containers int `json:"target_num_containers"` 415 | Uuid string `json:"uuid"` 416 | Working_dir string `json:"working_dir"` 417 | } 418 | 419 | type ServiceBinding struct { 420 | Container_path string `json:"container_path"` 421 | Host_path string `json:"host_path"` 422 | Rewritable bool `json:"rewritable"` 423 | Volumes_from string `json:"volume_from"` 424 | } 425 | 426 | type ServiceCreateRequest struct { 427 | Autodestroy string `json:"autodestroy,omitempty"` 428 | Autoredeploy bool `json:"autoredeploy,omitempty"` 429 | Autorestart string `json:"autorestart,omitempty"` 430 | Bindings []ServiceBinding `json:"bindings,omitempty"` 431 | Container_envvars []ContainerEnvvar `json:"container_envvars,omitempty"` 432 | Container_ports []ContainerPortInfo `json:"container_ports,omitempty"` 433 | Deployment_strategy string `json:"deployment_strategy,omitempty"` 434 | Entrypoint string `json:"entrypoint,omitempty"` 435 | Image string `json:"image,omitempty"` 436 | Linked_to_service []ServiceLinkInfo `json:"linked_to_service,omitempty"` 437 | Name string `json:"name,omitempty"` 438 | Net string `json:"net,omitempty"` 439 | Pid string `json:"pid,omitempty"` 440 | Privileged bool `json:"privileged,omitempty"` 441 | Roles []string `json:"roles,omitempty"` 442 | Run_command string `json:"run_command,omitempty"` 443 | Sequential_deployment bool `json:"sequential_deployment,omitempty"` 444 | Tags []string `json:"tags,omitempty"` 445 | Target_num_containers int `json:"target_num_containers,omitempty"` 446 | Working_dir string `json:"working_dir,omitempty"` 447 | } 448 | 449 | type ServiceLinkInfo struct { 450 | From_service string `json:"from_service"` 451 | Name string `json:"name"` 452 | To_service string `json:"to_service"` 453 | } 454 | 455 | type ServiceTag struct { 456 | Name string `json:"name"` 457 | } 458 | 459 | type StackListResponse struct { 460 | Meta Meta `json:"meta"` 461 | Objects []Stack `json:"objects"` 462 | } 463 | 464 | type Stack struct { 465 | Deployed_datetime string `json:"deployed_datetime"` 466 | Destroyed_datetime string `json:"destroyed_datetime` 467 | Name string `json:"name"` 468 | Resource_uri string `json:"resource_uri` 469 | Services []string `json:"services"` 470 | State string `json:"state"` 471 | Synchronized bool `json:"synchronized"` 472 | Uuid string `json:"uuid"` 473 | } 474 | 475 | type StackCreateRequest struct { 476 | Name string `json:"name,omitempty"` 477 | Services []ServiceCreateRequest `json:"services,omitempty"` 478 | } 479 | 480 | type Token struct { 481 | Token string `json:"token"` 482 | } 483 | 484 | type TriggerListResponse struct { 485 | Meta Meta `json:"meta"` 486 | Objects []Trigger `json:"objects"` 487 | } 488 | 489 | type Trigger struct { 490 | Url string `json:"url,omitempty"` 491 | Name string `json:"name"` 492 | Operation string `json:"operation"` 493 | Resource_uri string `json:"resource_uri,omitempty"` 494 | } 495 | 496 | type TriggerCreateRequest struct { 497 | Name string `json:"name"` 498 | Operation string `json:"operation"` 499 | } 500 | 501 | type VolumeListResponse struct { 502 | Meta Meta `json:"meta"` 503 | Objects []Volume `json:"objects"` 504 | } 505 | 506 | type Volume struct { 507 | Containers []string `json:"containers"` 508 | Node string `json:"node"` 509 | Resource_uri string `json:"resource_uri"` 510 | State string `json:"state"` 511 | Uuid string `json:"uuid"` 512 | Volume_group string `json:"volume_group"` 513 | } 514 | 515 | type VolumeGroupListResponse struct { 516 | Meta Meta `json:"meta"` 517 | Objects []VolumeGroup `json:"objects"` 518 | } 519 | 520 | type VolumeGroup struct { 521 | Name string `json:"name"` 522 | Resource_uri string `json:"resource_uri"` 523 | Services []string `json:"services"` 524 | State string `json:"state"` 525 | Uuid string `json:"uuid"` 526 | Volume []string `json:"volume"` 527 | } 528 | 529 | type VolumePath struct { 530 | Container_path string `json:"container_path"` 531 | } 532 | --------------------------------------------------------------------------------