├── .gitignore ├── LICENSE ├── README.md ├── account_service.go ├── account_service_test.go ├── api_key_test.go ├── api_service.go ├── api_service_test.go ├── avail_service.go ├── avail_service_test.go ├── custombool.go ├── customstring.go ├── doc.go ├── examples └── client.go ├── image_service.go ├── image_service_test.go ├── linode_config_service.go ├── linode_config_service_test.go ├── linode_disk_service.go ├── linode_disk_service_test.go ├── linode_ip_service.go ├── linode_ip_service_test.go ├── linode_job_service.go ├── linode_job_service_test.go ├── linode_service.go ├── linode_service_test.go ├── linodego.go ├── linodego_test.go ├── test_service.go ├── test_service_test.go ├── time.go └── types.go /.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 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 TH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linodego 2 | 3 | ### Overview 4 | 5 | Package linodego is an unofficial Go client implementation for [Linode](http://www.linode.com) API. 6 | Check the Linode API documentation for details: 7 | [https://www.linode.com/api](https://www.linode.com/api) 8 | 9 | Current API implementation supports using api_key request parameter. All 10 | requests are sent using GET methods. While it is also possible to use POST method by setting UsePost, Linode API seems to return response with keys captialized. Unmarshaling such response isn't fully tested. 11 | 12 | `TODO`: Batch request is not implemented yet. 13 | 14 | ### Example 15 | 16 | Check examples/client.go for sample usage. Note that you must supple API Key 17 | in examples/client.go before running the program. Get API from [https://manager.linode.com/profile/api](https://manager.linode.com/profile/api) 18 | 19 | 20 | ``` 21 | go run examples/client.go 22 | ``` 23 | 24 | ### Test 25 | Update API Key in api_key_test.go, then run: 26 | 27 | ``` 28 | go test 29 | ``` 30 | 31 | ### Installation 32 | 33 | ``` 34 | go get "github.com/taoh/linodego" 35 | ``` 36 | 37 | ### Package 38 | 39 | ``` 40 | import "github.com/taoh/linodego" 41 | ``` 42 | 43 | ### Documentation 44 | See [GoDoc](http://godoc.org/github.com/taoh/linodego) 45 | 46 | ### License 47 | The MIT License (MIT) 48 | 49 | Copyright (c) 2015 TH 50 | 51 | -------------------------------------------------------------------------------- /account_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Account Service 10 | type AccountService struct { 11 | client *Client 12 | } 13 | 14 | // Response for account.estimateinvoice API 15 | type EstimateInvoiceResponse struct { 16 | Response 17 | EstimateInvoice EstimateInvoice 18 | } 19 | 20 | // Response for account.info API 21 | type AccountInfoResponse struct { 22 | Response 23 | AccountInfo AccountInfo 24 | } 25 | 26 | // Estimate Invoice 27 | func (t *AccountService) EstimateInvoice(mode string, planId int, paymentTerm int, linodeId int) (*EstimateInvoiceResponse, error) { 28 | u := &url.Values{} 29 | u.Add("mode", mode) 30 | u.Add("PlanId", strconv.Itoa(planId)) 31 | u.Add("LinodeId", strconv.Itoa(linodeId)) 32 | 33 | if (mode == "linode_new") || (mode == "nodebalancer_new") { 34 | u.Add("PaymentTerm", strconv.Itoa(paymentTerm)) 35 | } 36 | //TODO: add more validations for params combinations 37 | v := EstimateInvoiceResponse{} 38 | if err := t.client.do("account.estimateinvoice", u, &v.Response); err != nil { 39 | return nil, err 40 | } 41 | 42 | if err := json.Unmarshal(v.RawData, &v.EstimateInvoice); err != nil { 43 | return nil, err 44 | } 45 | return &v, nil 46 | } 47 | 48 | // Get Account Info 49 | func (t *AccountService) Info() (*AccountInfoResponse, error) { 50 | u := &url.Values{} 51 | v := AccountInfoResponse{} 52 | if err := t.client.do("account.info", u, &v.Response); err != nil { 53 | return nil, err 54 | } 55 | 56 | if err := json.Unmarshal(v.RawData, &v.AccountInfo); err != nil { 57 | return nil, err 58 | } 59 | return &v, nil 60 | } 61 | -------------------------------------------------------------------------------- /account_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "testing" 6 | ) 7 | 8 | func TestAccountEstimateInvoice(t *testing.T) { 9 | client := NewClient(APIKey, nil) 10 | 11 | v, err := client.Account.EstimateInvoice("linode_new", 2, 1, -1) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | log.Debugf("Estimate Invoice: %s, %f", v.EstimateInvoice.InvoiceTo, v.EstimateInvoice.Amount) 17 | } 18 | 19 | func TestAccountInfo(t *testing.T) { 20 | client := NewClient(APIKey, nil) 21 | 22 | v, err := client.Account.Info() 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | log.Debugf("Account Info: %f, %b", v.AccountInfo.Balance, v.AccountInfo.Managed) 28 | } 29 | -------------------------------------------------------------------------------- /api_key_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | const ( 4 | // Supply your unique secret API key 5 | APIKey = "[API Key]" 6 | ) 7 | -------------------------------------------------------------------------------- /api_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // API Service 9 | type ApiService struct { 10 | client *Client 11 | } 12 | 13 | // Response for api.spec Service 14 | type ApiResponse struct { 15 | Response 16 | Data map[string]interface{} 17 | } 18 | 19 | // Get API Specs 20 | func (t *ApiService) Spec(v *ApiResponse) error { 21 | u := &url.Values{} 22 | if err := t.client.do("api.spec", u, &v.Response); err != nil { 23 | return err 24 | } 25 | v.Data = map[string]interface{}{} 26 | if err := json.Unmarshal(v.RawData, &v.Data); err != nil { 27 | return err 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /api_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | // import ( 4 | // "testing" 5 | // ) 6 | 7 | // func TestApiSpec(t *testing.T) { 8 | // client := NewClient(APIKey, nil) 9 | // v := &ApiResponse{} 10 | // if err := client.Api.Spec(v); err != nil { 11 | // t.Fatal(err) 12 | // } 13 | // } 14 | -------------------------------------------------------------------------------- /avail_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Avail Service 10 | type AvailService struct { 11 | client *Client 12 | } 13 | 14 | // Response for avail.datacenters API 15 | type AvailDataCentersResponse struct { 16 | Response 17 | DataCenters []DataCenter 18 | } 19 | 20 | // Response for avail.distributions API 21 | type AvailDistributionsResponse struct { 22 | Response 23 | Distributions []Distribution 24 | } 25 | 26 | // Response for avail.kernels API 27 | type KernelsResponse struct { 28 | Response 29 | Kernels []Kernel 30 | } 31 | 32 | // Response for avail.linodeplans API 33 | type LinodePlansResponse struct { 34 | Response 35 | LinodePlans []LinodePlan 36 | } 37 | 38 | // Response for avail.nodebalancers API 39 | type NodeBalancersResponse struct { 40 | Response 41 | NodeBalancers []NodeBalancer 42 | } 43 | 44 | // Response for avail.stackscripts API 45 | type StackScriptsResponse struct { 46 | Response 47 | StackScripts []StackScript 48 | } 49 | 50 | // Get DataCenters 51 | func (t *AvailService) DataCenters() (*AvailDataCentersResponse, error) { 52 | u := &url.Values{} 53 | v := AvailDataCentersResponse{} 54 | if err := t.client.do("avail.datacenters", u, &v.Response); err != nil { 55 | return nil, err 56 | } 57 | v.DataCenters = make([]DataCenter, 5) 58 | if err := json.Unmarshal(v.RawData, &v.DataCenters); err != nil { 59 | return nil, err 60 | } 61 | return &v, nil 62 | } 63 | 64 | // Get Distributions 65 | func (t *AvailService) Distributions() (*AvailDistributionsResponse, error) { 66 | u := &url.Values{} 67 | v := AvailDistributionsResponse{} 68 | if err := t.client.do("avail.distributions", u, &v.Response); err != nil { 69 | return nil, err 70 | } 71 | v.Distributions = make([]Distribution, 5) 72 | if err := json.Unmarshal(v.RawData, &v.Distributions); err != nil { 73 | return nil, err 74 | } 75 | return &v, nil 76 | } 77 | 78 | // Get Kernels 79 | func (t *AvailService) Kernels() (*KernelsResponse, error) { 80 | u := &url.Values{} 81 | v := KernelsResponse{} 82 | if err := t.client.do("avail.kernels", u, &v.Response); err != nil { 83 | return nil, err 84 | } 85 | v.Kernels = make([]Kernel, 5) 86 | if err := json.Unmarshal(v.RawData, &v.Kernels); err != nil { 87 | return nil, err 88 | } 89 | return &v, nil 90 | } 91 | 92 | // Get filtered Kernels 93 | func (t *AvailService) FilterKernels(isxen int, iskvm int) (*KernelsResponse, error) { 94 | params := &url.Values{} 95 | v := KernelsResponse{} 96 | params.Add("isxen", strconv.Itoa(isxen)) 97 | params.Add("iskvm", strconv.Itoa(iskvm)) 98 | 99 | if err := t.client.do("avail.kernels", params, &v.Response); err != nil { 100 | return nil, err 101 | } 102 | v.Kernels = make([]Kernel, 5) 103 | if err := json.Unmarshal(v.RawData, &v.Kernels); err != nil { 104 | return nil, err 105 | } 106 | return &v, nil 107 | } 108 | 109 | // Get Linode Plans 110 | func (t *AvailService) LinodePlans() (*LinodePlansResponse, error) { 111 | u := &url.Values{} 112 | v := LinodePlansResponse{} 113 | if err := t.client.do("avail.linodeplans", u, &v.Response); err != nil { 114 | return nil, err 115 | } 116 | v.LinodePlans = make([]LinodePlan, 5) 117 | if err := json.Unmarshal(v.RawData, &v.LinodePlans); err != nil { 118 | return nil, err 119 | } 120 | return &v, nil 121 | } 122 | 123 | // Get Node Balancers 124 | func (t *AvailService) NodeBalancers() (*NodeBalancersResponse, error) { 125 | u := &url.Values{} 126 | v := NodeBalancersResponse{} 127 | if err := t.client.do("avail.nodebalancers", u, &v.Response); err != nil { 128 | return nil, err 129 | } 130 | v.NodeBalancers = make([]NodeBalancer, 5) 131 | if err := json.Unmarshal(v.RawData, &v.NodeBalancers); err != nil { 132 | return nil, err 133 | } 134 | return &v, nil 135 | } 136 | 137 | // Get All Stackscripts 138 | func (t *AvailService) StackScripts() (*StackScriptsResponse, error) { 139 | u := &url.Values{} 140 | v := StackScriptsResponse{} 141 | if err := t.client.do("avail.stackscripts", u, &v.Response); err != nil { 142 | return nil, err 143 | } 144 | v.StackScripts = make([]StackScript, 5) 145 | if err := json.Unmarshal(v.RawData, &v.StackScripts); err != nil { 146 | return nil, err 147 | } 148 | return &v, nil 149 | } 150 | -------------------------------------------------------------------------------- /avail_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "testing" 6 | ) 7 | 8 | func TestAvailDataCenters(t *testing.T) { 9 | client := NewClient(APIKey, nil) 10 | 11 | v, err := client.Avail.DataCenters() 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | for _, dataCenter := range v.DataCenters { 17 | log.Debugf("Data Center: %d, %s, %s", dataCenter.DataCenterId, dataCenter.Location, dataCenter.Abbr) 18 | } 19 | } 20 | 21 | func TestAvailDistributions(t *testing.T) { 22 | client := NewClient(APIKey, nil) 23 | 24 | v, err := client.Avail.Distributions() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | for _, distribution := range v.Distributions { 30 | log.Debugf("Distribution: %s, %b, %s", distribution.Label, distribution.Is64Bit, distribution.CreatedDt) 31 | } 32 | } 33 | 34 | func TestAvailKernels(t *testing.T) { 35 | client := NewClient(APIKey, nil) 36 | 37 | v, err := client.Avail.Kernels() 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | for _, kernel := range v.Kernels { 43 | log.Debugf("Kernel: %s, %d, %d, %d", kernel.Label, kernel.IsXen, kernel.IsPVOPS, kernel.KernelId) 44 | } 45 | } 46 | 47 | func TestFilterAvailKernels(t *testing.T) { 48 | client := NewClient(APIKey, nil) 49 | 50 | v, err := client.Avail.FilterKernels(0, 1) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | for _, kernel := range v.Kernels { 56 | log.Debugf("Kernel: %s, %d, %d, %d", kernel.Label, kernel.IsXen, kernel.IsPVOPS, kernel.KernelId) 57 | } 58 | } 59 | 60 | func TestFilterAvailKernelsOnKvm(t *testing.T) { 61 | client := NewClient(APIKey, nil) 62 | 63 | v, err := client.Avail.FilterKernels(0, 1) 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | grub2 := false 69 | directDisk := false 70 | for _, kernel := range v.Kernels { 71 | if kernel.Label.String() == "GRUB 2" { 72 | grub2 = true 73 | } 74 | if kernel.Label.String() == "Direct Disk" { 75 | directDisk = true 76 | } 77 | } 78 | if grub2 == false || directDisk == false { 79 | t.Fail() 80 | } 81 | } 82 | 83 | func TestAvailLinodePlans(t *testing.T) { 84 | client := NewClient(APIKey, nil) 85 | 86 | v, err := client.Avail.LinodePlans() 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | 91 | for _, plan := range v.LinodePlans { 92 | log.Debugf("Linode Plans: %d, %s, %f, %d, %d", plan.PlanId, plan.Label, plan.Price, plan.Cores, plan.RAM) 93 | } 94 | } 95 | 96 | func TestAvailNodeBalancers(t *testing.T) { 97 | client := NewClient(APIKey, nil) 98 | 99 | v, err := client.Avail.NodeBalancers() 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | 104 | for _, nodeBalancer := range v.NodeBalancers { 105 | log.Debugf("NodeBalancer: %f, %f, %d", nodeBalancer.Hourly, nodeBalancer.Monthly, nodeBalancer.Connections) 106 | } 107 | } 108 | 109 | // func TestAvailStackScripts(t *testing.T) { 110 | // client := NewClient(APIKey, nil) 111 | 112 | // v, err := client.Avail.StackScripts() 113 | // if err != nil { 114 | // t.Fatal(err) 115 | // } 116 | 117 | // // for _, stackScript := range v.StackScripts { 118 | // // log.Debugf("StackScripts: %s, %d, %d", stackScript.Label, stackScript.DeploymentsTotal, stackScript.DeploymentsActive) 119 | // // } 120 | // } 121 | -------------------------------------------------------------------------------- /custombool.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // CustomBool is a type to handle Linode's insistance of using ints as boolean values. 8 | type CustomBool struct { 9 | Bool bool 10 | } 11 | 12 | func (cb *CustomBool) UnmarshalJSON(b []byte) error { 13 | if len(b) != 1 { 14 | return fmt.Errorf("Unable to marshal value with length %d into a CustomBool.", len(b)) 15 | } 16 | if int(b[0]) == 0 { 17 | cb.Bool = false 18 | } else if int(b[0]) == 1 { 19 | cb.Bool = true 20 | } 21 | return nil 22 | } 23 | 24 | func (cb *CustomBool) MarshalJSON() ([]byte, error) { 25 | if cb == nil { 26 | return []byte{}, fmt.Errorf("Unable to marshal nil value for CustomBool") 27 | } 28 | if cb.Bool { 29 | return []byte{1}, nil 30 | } else { 31 | return []byte{0}, nil 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /customstring.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import () 4 | 5 | // A special class to handle marshaling string response from Linode 6 | // As sometimes Linode returns integer instead of string from API 7 | // https://github.com/taoh/linodego/issues/1 8 | type CustomString struct { 9 | string 10 | } 11 | 12 | func (cs *CustomString) UnmarshalJSON(b []byte) (err error) { 13 | // if bytes are not surrounded by double quotes 14 | if b[0] != '"' && b[len(b)-1] != '"' { 15 | cs.string = string(append(append([]byte{'"'}, b...), '"')) 16 | } else { 17 | cs.string = string(b) 18 | } 19 | return nil 20 | } 21 | 22 | func (cs *CustomString) MarshalJSON() ([]byte, error) { 23 | return []byte(cs.string), nil 24 | } 25 | 26 | func (cs *CustomString) String() string { 27 | // remove double quotes 28 | return cs.string[1 : len(cs.string)-1] 29 | } 30 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | /* 4 | Package linodego is an unofficial Go client implementation for Linode API. 5 | Check the Linode API documentation for details: 6 | https://www.linode.com/api 7 | 8 | Current API implementation supports using api_key request parameter. All 9 | requests are sent using POST methods. 10 | 11 | TODO: Batching requests is not implemented yet. 12 | 13 | Check examples/client.go for sample usage. Note that you must replace [SUPPLY YOUR API KEY HERE] 14 | in examples/client.go before running the program. 15 | */ 16 | -------------------------------------------------------------------------------- /examples/client.go: -------------------------------------------------------------------------------- 1 | // +build example 2 | package main 3 | 4 | import ( 5 | log "github.com/sirupsen/logrus" 6 | "github.com/taoh/linodego" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | if os.Getenv("DEBUG") == "true" { 12 | log.SetLevel(log.DebugLevel) // set debug level 13 | } 14 | 15 | apiKey := "[API Key]" 16 | client := linodego.NewClient(apiKey, nil) 17 | 18 | // Create a linode 19 | v, err := client.Linode.Create(2, 1, 1) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | linodeId := v.LinodeId.LinodeId 24 | log.Infof("Created linode: %d", linodeId) 25 | 26 | // Get IP Address 27 | v2, err := client.Ip.List(linodeId, -1) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | fullIpAddress := v2.FullIPAddresses[0] 32 | log.Infof("IP Address: Id: %d, Address: %s", fullIpAddress.IPAddressId, fullIpAddress.IPAddress) 33 | 34 | // Update label, https://github.com/taoh/linodego/issues/1 35 | args := make(map[string]interface{}) 36 | args["Label"] = 12345 37 | _, err = client.Linode.Update(linodeId, args) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | log.Infof("Updated Linode: Label %s", args) 42 | 43 | // List linode 44 | v3, err := client.Linode.List(linodeId) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | linode := v3.Linodes[0] 49 | log.Infof("List Linode: %d, Label: %s, Status: %d", linode.LinodeId, linode.Label, linode.Status) 50 | 51 | // Shutdown the linode 52 | v4, err := client.Linode.Shutdown(linodeId) 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | job := v4.Job 57 | log.Infof("Shutdown linode: %s", job) 58 | 59 | // Delete the linode 60 | _, err = client.Linode.Delete(linodeId, false) 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | 65 | log.Infof("Deleted linode: %s", linodeId) 66 | } 67 | -------------------------------------------------------------------------------- /image_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Image Service 10 | type ImageService struct { 11 | client *Client 12 | } 13 | 14 | // Response for image.list API 15 | type ImagesListResponse struct { 16 | Response 17 | Images []Image 18 | } 19 | 20 | // Response for image.update or image.delete API 21 | type ImageResponse struct { 22 | Response 23 | Image Image 24 | } 25 | 26 | // List all images 27 | func (t *ImageService) List() (*ImagesListResponse, error) { 28 | u := &url.Values{} 29 | v := ImagesListResponse{} 30 | if err := t.client.do("image.list", u, &v.Response); err != nil { 31 | return nil, err 32 | } 33 | 34 | v.Images = make([]Image, 5) 35 | if err := json.Unmarshal(v.RawData, &v.Images); err != nil { 36 | return nil, err 37 | } 38 | return &v, nil 39 | } 40 | 41 | // Update given Image 42 | func (t *ImageService) Update(imageId int, label string, description string) (*ImageResponse, error) { 43 | u := &url.Values{} 44 | u.Add("ImageID", strconv.Itoa(imageId)) 45 | if label != "" { 46 | u.Add("label", label) 47 | } 48 | if description != "" { 49 | u.Add("description", description) 50 | } 51 | 52 | v := ImageResponse{} 53 | if err := t.client.do("image.update", u, &v.Response); err != nil { 54 | return nil, err 55 | } 56 | 57 | v.Image = Image{} 58 | if err := json.Unmarshal(v.RawData, &v.Image); err != nil { 59 | return nil, err 60 | } 61 | return &v, nil 62 | } 63 | 64 | // Delete given Image 65 | func (t *ImageService) Delete(imageId int) (*ImageResponse, error) { 66 | u := &url.Values{} 67 | u.Add("ImageID", strconv.Itoa(imageId)) 68 | v := ImageResponse{} 69 | if err := t.client.do("image.delete", u, &v.Response); err != nil { 70 | return nil, err 71 | } 72 | 73 | v.Image = Image{} 74 | if err := json.Unmarshal(v.RawData, &v.Image); err != nil { 75 | return nil, err 76 | } 77 | return &v, nil 78 | } 79 | -------------------------------------------------------------------------------- /image_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "testing" 6 | ) 7 | 8 | func TestListImages(t *testing.T) { 9 | client := NewClient(APIKey, nil) 10 | 11 | v, err := client.Image.List() 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | for _, image := range v.Images { 17 | log.Debugf("Kernel: %s, %s, %s", image.Label, image.Status, image.CreateDt) 18 | } 19 | } 20 | 21 | func TestUpdateImage(t *testing.T) { 22 | client := NewClient(APIKey, nil) 23 | createResp, err := client.Linode.Create(2, 1, 1) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | // id of the freshly created linode 28 | linodeId := createResp.LinodeId.LinodeId 29 | 30 | // create simple rootfs 31 | _, err = client.Disk.Create(linodeId, "ext4", "rootfs", 19286, nil) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | // list the disks now associated with this linode 37 | diskListResp, err := client.Disk.List(linodeId, 0) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | // makes the assumption that only one disk was created 43 | diskId := diskListResp.Disks[0].DiskId 44 | 45 | // imagize the disk that was just created, unfortunately just responds with job id 46 | _, err = client.Disk.Imagize(linodeId, diskId, "Part of testing", "TestingImage") 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | // this takes a while 52 | WaitForPendingJobs(client, linodeId) 53 | 54 | // list images for this linode account 55 | imageListResp, err := client.Image.List() 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | // retrieve image 61 | var imageid int 62 | for _, image := range imageListResp.Images { 63 | if image.Description == "Part of testing" { 64 | imageid = image.ImageId 65 | break 66 | } 67 | } 68 | 69 | imageUpdateResponse, err := client.Image.Update(imageid, "Updated label", "Updated description") 70 | if err != nil { 71 | t.Fatal(err) 72 | } 73 | 74 | image := imageUpdateResponse.Image 75 | if image.Description != "Updated description" { 76 | t.Fatal(image.Description) 77 | } 78 | log.Debugf("Kernel: %s, %s, %s", image.Label, image.Status, image.CreateDt) 79 | 80 | // teardown because money 81 | _, err = client.Image.Delete(imageid) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | _, err = client.Linode.Delete(linodeId, true) 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /linode_config_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Linode Config Service 10 | type LinodeConfigService struct { 11 | client *Client 12 | } 13 | 14 | // Response for linode.config.list API 15 | type LinodeConfigListResponse struct { 16 | Response 17 | LinodeConfigs []LinodeConfig 18 | } 19 | 20 | // Response for general config APIs 21 | type LinodeConfigResponse struct { 22 | Response 23 | LinodeConfigId LinodeConfigId 24 | } 25 | 26 | // Get Config List. If configId is greater than 0, limit results to given config. 27 | func (t *LinodeConfigService) List(linodeId int, configId int) (*LinodeConfigListResponse, error) { 28 | u := &url.Values{} 29 | if configId > 0 { 30 | u.Add("ConfigID", strconv.Itoa(configId)) 31 | } 32 | u.Add("LinodeID", strconv.Itoa(linodeId)) 33 | v := LinodeConfigListResponse{} 34 | if err := t.client.do("linode.config.list", u, &v.Response); err != nil { 35 | return nil, err 36 | } 37 | 38 | v.LinodeConfigs = make([]LinodeConfig, 5) 39 | if err := json.Unmarshal(v.RawData, &v.LinodeConfigs); err != nil { 40 | return nil, err 41 | } 42 | return &v, nil 43 | } 44 | 45 | // Create Config 46 | func (t *LinodeConfigService) Create(linodeId int, kernelId int, label string, args map[string]string) (*LinodeConfigResponse, error) { 47 | u := &url.Values{} 48 | u.Add("LinodeID", strconv.Itoa(linodeId)) 49 | u.Add("KernelID", strconv.Itoa(kernelId)) 50 | u.Add("Label", label) 51 | // add optional parameters 52 | processOptionalArgs(args, u) 53 | v := LinodeConfigResponse{} 54 | if err := t.client.do("linode.config.create", u, &v.Response); err != nil { 55 | return nil, err 56 | } 57 | 58 | if err := json.Unmarshal(v.RawData, &v.LinodeConfigId); err != nil { 59 | return nil, err 60 | } 61 | return &v, nil 62 | } 63 | 64 | // Update Config. See https://www.linode.com/api/linode/linode.config.update for allowed arguments. 65 | func (t *LinodeConfigService) Update(configId int, linodeId int, kernelId int, args map[string]string) (*LinodeConfigResponse, error) { 66 | u := &url.Values{} 67 | u.Add("ConfigID", strconv.Itoa(configId)) 68 | if linodeId > 0 { 69 | u.Add("LinodeID", strconv.Itoa(linodeId)) 70 | } 71 | if kernelId > 0 { 72 | u.Add("KernelID", strconv.Itoa(kernelId)) 73 | } 74 | 75 | // add optional parameters 76 | processOptionalArgs(args, u) 77 | v := LinodeConfigResponse{} 78 | if err := t.client.do("linode.config.update", u, &v.Response); err != nil { 79 | return nil, err 80 | } 81 | 82 | if err := json.Unmarshal(v.RawData, &v.LinodeConfigId); err != nil { 83 | return nil, err 84 | } 85 | return &v, nil 86 | } 87 | 88 | // Delete Config 89 | func (t *LinodeConfigService) Delete(linodeId int, configId int) (*LinodeConfigResponse, error) { 90 | u := &url.Values{} 91 | u.Add("LinodeID", strconv.Itoa(linodeId)) 92 | u.Add("ConfigID", strconv.Itoa(configId)) 93 | v := LinodeConfigResponse{} 94 | if err := t.client.do("linode.config.delete", u, &v.Response); err != nil { 95 | return nil, err 96 | } 97 | 98 | if err := json.Unmarshal(v.RawData, &v.LinodeConfigId); err != nil { 99 | return nil, err 100 | } 101 | return &v, nil 102 | } 103 | 104 | func processOptionalArgs(args map[string]string, parameters *url.Values) { 105 | for k, v := range args { 106 | if k == "helper_xen" { 107 | parameters.Add("helper_distro", v) 108 | } 109 | parameters.Add(k, v) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /linode_config_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | -------------------------------------------------------------------------------- /linode_disk_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Linode Disk Service 10 | type LinodeDiskService struct { 11 | client *Client 12 | } 13 | 14 | // Response for linode.disk.list API 15 | type LinodeDiskListResponse struct { 16 | Response 17 | Disks []Disk 18 | } 19 | 20 | // Response for general Disk jobs APIs 21 | type LinodeDiskJobResponse struct { 22 | Response 23 | DiskJob DiskJob 24 | } 25 | 26 | // List all disks. If diskId is greater than 0, limit the results to given disk. 27 | func (t *LinodeDiskService) List(linodeId int, diskId int) (*LinodeDiskListResponse, error) { 28 | u := &url.Values{} 29 | u.Add("LinodeID", strconv.Itoa(linodeId)) 30 | if diskId > 0 { 31 | u.Add("DiskID", strconv.Itoa(diskId)) 32 | } 33 | v := LinodeDiskListResponse{} 34 | if err := t.client.do("linode.disk.list", u, &v.Response); err != nil { 35 | return nil, err 36 | } 37 | 38 | v.Disks = make([]Disk, 5) 39 | if err := json.Unmarshal(v.RawData, &v.Disks); err != nil { 40 | return nil, err 41 | } 42 | return &v, nil 43 | } 44 | 45 | // Create disk 46 | func (t *LinodeDiskService) Create(linodeId int, diskType string, label string, size int, args map[string]string) (*LinodeDiskJobResponse, error) { 47 | u := &url.Values{} 48 | u.Add("LinodeID", strconv.Itoa(linodeId)) 49 | u.Add("Size", strconv.Itoa(size)) 50 | u.Add("Type", diskType) 51 | u.Add("Label", label) 52 | // add optional parameters 53 | for k, v := range args { 54 | u.Add(k, v) 55 | } 56 | v := LinodeDiskJobResponse{} 57 | if err := t.client.do("linode.disk.create", u, &v.Response); err != nil { 58 | return nil, err 59 | } 60 | 61 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 62 | return nil, err 63 | } 64 | return &v, nil 65 | } 66 | 67 | // Create from Distribution 68 | func (t *LinodeDiskService) CreateFromDistribution(distributionId int, linodeId int, label string, size int, args map[string]string) (*LinodeDiskJobResponse, error) { 69 | u := &url.Values{} 70 | u.Add("DistributionID", strconv.Itoa(distributionId)) 71 | u.Add("LinodeID", strconv.Itoa(linodeId)) 72 | u.Add("Size", strconv.Itoa(size)) 73 | u.Add("Label", label) 74 | // add optional parameters 75 | for k, v := range args { 76 | u.Add(k, v) 77 | } 78 | v := LinodeDiskJobResponse{} 79 | if err := t.client.do("linode.disk.createFromDistribution", u, &v.Response); err != nil { 80 | return nil, err 81 | } 82 | 83 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 84 | return nil, err 85 | } 86 | return &v, nil 87 | } 88 | 89 | // Create from image 90 | func (t *LinodeDiskService) CreateFromImage(imageId int, linodeId int, label string, size int, args map[string]string) (*LinodeDiskJobResponse, error) { 91 | u := &url.Values{} 92 | u.Add("ImageID", strconv.Itoa(imageId)) 93 | u.Add("LinodeID", strconv.Itoa(linodeId)) 94 | if size > 0 { 95 | u.Add("Size", strconv.Itoa(size)) 96 | } 97 | u.Add("Label", label) 98 | // add optional parameters 99 | for k, v := range args { 100 | u.Add(k, v) 101 | } 102 | v := LinodeDiskJobResponse{} 103 | if err := t.client.do("linode.disk.createfromimage", u, &v.Response); err != nil { 104 | return nil, err 105 | } 106 | 107 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 108 | return nil, err 109 | } 110 | return &v, nil 111 | } 112 | 113 | // Create from stackscript 114 | func (t *LinodeDiskService) CreateFromStackscript( 115 | stackScriptId int, linodeId int, label string, 116 | stackScriptUDFResponses string, 117 | distributionId int, size int, rootPass string, 118 | args map[string]string) (*LinodeDiskJobResponse, error) { 119 | u := &url.Values{} 120 | u.Add("StackScriptID", strconv.Itoa(stackScriptId)) 121 | u.Add("LinodeID", strconv.Itoa(linodeId)) 122 | u.Add("StackScriptUDFResponses", stackScriptUDFResponses) 123 | u.Add("DistributionID", strconv.Itoa(distributionId)) 124 | u.Add("rootPass", rootPass) 125 | u.Add("Size", strconv.Itoa(size)) 126 | u.Add("Label", label) 127 | // add optional parameters 128 | for k, v := range args { 129 | u.Add(k, v) 130 | } 131 | v := LinodeDiskJobResponse{} 132 | if err := t.client.do("linode.disk.createfromstackscript", u, &v.Response); err != nil { 133 | return nil, err 134 | } 135 | 136 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 137 | return nil, err 138 | } 139 | return &v, nil 140 | } 141 | 142 | // Delete disk 143 | func (t *LinodeDiskService) Delete(linodeId int, diskId int) (*LinodeDiskJobResponse, error) { 144 | u := &url.Values{} 145 | u.Add("DiskID", strconv.Itoa(diskId)) 146 | u.Add("LinodeID", strconv.Itoa(linodeId)) 147 | v := LinodeDiskJobResponse{} 148 | if err := t.client.do("linode.disk.delete", u, &v.Response); err != nil { 149 | return nil, err 150 | } 151 | 152 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 153 | return nil, err 154 | } 155 | return &v, nil 156 | } 157 | 158 | // Duplicate Disk 159 | func (t *LinodeDiskService) Duplicate(linodeId int, diskId int) (*LinodeDiskJobResponse, error) { 160 | u := &url.Values{} 161 | u.Add("DiskID", strconv.Itoa(diskId)) 162 | u.Add("LinodeID", strconv.Itoa(linodeId)) 163 | v := LinodeDiskJobResponse{} 164 | if err := t.client.do("linode.disk.duplicate", u, &v.Response); err != nil { 165 | return nil, err 166 | } 167 | 168 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 169 | return nil, err 170 | } 171 | return &v, nil 172 | } 173 | 174 | // Imagize a disk 175 | func (t *LinodeDiskService) Imagize(linodeId int, diskId int, description string, label string) (*LinodeDiskJobResponse, error) { 176 | u := &url.Values{} 177 | u.Add("DiskID", strconv.Itoa(diskId)) 178 | u.Add("LinodeID", strconv.Itoa(linodeId)) 179 | if description != "" { 180 | u.Add("Description", description) 181 | } 182 | if label != "nil" { 183 | u.Add("Label", label) 184 | } 185 | v := LinodeDiskJobResponse{} 186 | if err := t.client.do("linode.disk.imagize", u, &v.Response); err != nil { 187 | return nil, err 188 | } 189 | 190 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 191 | return nil, err 192 | } 193 | return &v, nil 194 | } 195 | 196 | // Resize a disk 197 | func (t *LinodeDiskService) Resize(linodeId int, diskId int, size int) (*LinodeDiskJobResponse, error) { 198 | u := &url.Values{} 199 | u.Add("DiskID", strconv.Itoa(diskId)) 200 | u.Add("LinodeID", strconv.Itoa(linodeId)) 201 | u.Add("size", strconv.Itoa(size)) 202 | v := LinodeDiskJobResponse{} 203 | if err := t.client.do("linode.disk.resize", u, &v.Response); err != nil { 204 | return nil, err 205 | } 206 | 207 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 208 | return nil, err 209 | } 210 | return &v, nil 211 | } 212 | 213 | // Update a disk 214 | func (t *LinodeDiskService) Update(linodeId int, diskId int, label string, isReadOnly bool) (*LinodeDiskJobResponse, error) { 215 | u := &url.Values{} 216 | u.Add("DiskID", strconv.Itoa(diskId)) 217 | u.Add("LinodeID", strconv.Itoa(linodeId)) 218 | u.Add("Label", label) 219 | if isReadOnly { 220 | u.Add("isReadOnly", "1") 221 | } 222 | 223 | v := LinodeDiskJobResponse{} 224 | if err := t.client.do("linode.disk.update", u, &v.Response); err != nil { 225 | return nil, err 226 | } 227 | 228 | if err := json.Unmarshal(v.RawData, &v.DiskJob); err != nil { 229 | return nil, err 230 | } 231 | return &v, nil 232 | } 233 | -------------------------------------------------------------------------------- /linode_disk_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | -------------------------------------------------------------------------------- /linode_ip_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Linode IP Service 10 | type LinodeIPService struct { 11 | client *Client 12 | } 13 | 14 | // IP List Response 15 | type LinodeIPListResponse struct { 16 | Response 17 | FullIPAddresses []FullIPAddress 18 | } 19 | 20 | // IP Address Response 21 | type LinodeIPAddressResponse struct { 22 | Response 23 | IPAddress IPAddress 24 | } 25 | 26 | // IP Address with RDNS Response 27 | type LinodeRDNSIPAddressResponse struct { 28 | Response 29 | RDNSIPAddress RDNSIPAddress 30 | } 31 | 32 | // Full IP Address Response 33 | type LinodeLinodeIPAddressResponse struct { 34 | Response 35 | LinodeIPAddresses []LinodeIPAddress 36 | } 37 | 38 | // List All Ips. If linodeId or ipAddressId is less than 0, all IPs are returned. 39 | // Otherwise, limits the reuslt to the given linodeId, ipAddressId or both. 40 | func (t *LinodeIPService) List(linodeId int, ipAddressId int) (*LinodeIPListResponse, error) { 41 | u := &url.Values{} 42 | v := LinodeIPListResponse{} 43 | if linodeId > 0 { 44 | u.Add("LinodeID", strconv.Itoa(linodeId)) 45 | } 46 | if ipAddressId > 0 { 47 | u.Add("IPAddressID", strconv.Itoa(ipAddressId)) 48 | } 49 | if err := t.client.do("linode.ip.list", u, &v.Response); err != nil { 50 | return nil, err 51 | } 52 | 53 | v.FullIPAddresses = make([]FullIPAddress, 5) 54 | if err := json.Unmarshal(v.RawData, &v.FullIPAddresses); err != nil { 55 | return nil, err 56 | } 57 | return &v, nil 58 | } 59 | 60 | // Add Private IP 61 | func (t *LinodeIPService) AddPrivate(linodeId int) (*LinodeIPAddressResponse, error) { 62 | u := &url.Values{} 63 | u.Add("LinodeID", strconv.Itoa(linodeId)) 64 | v := LinodeIPAddressResponse{} 65 | if err := t.client.do("linode.ip.addprivate", u, &v.Response); err != nil { 66 | return nil, err 67 | } 68 | 69 | if err := json.Unmarshal(v.RawData, &v.IPAddress); err != nil { 70 | return nil, err 71 | } 72 | return &v, nil 73 | } 74 | 75 | // Add Public IP 76 | func (t *LinodeIPService) AddPublic(linodeId int) (*LinodeIPAddressResponse, error) { 77 | u := &url.Values{} 78 | u.Add("LinodeID", strconv.Itoa(linodeId)) 79 | v := LinodeIPAddressResponse{} 80 | if err := t.client.do("linode.ip.addpublic", u, &v.Response); err != nil { 81 | return nil, err 82 | } 83 | 84 | if err := json.Unmarshal(v.RawData, &v.IPAddress); err != nil { 85 | return nil, err 86 | } 87 | return &v, nil 88 | } 89 | 90 | // Set RDNS 91 | func (t *LinodeIPService) SetRDNS(linodeId int, hostname string) (*LinodeRDNSIPAddressResponse, error) { 92 | u := &url.Values{} 93 | u.Add("LinodeID", strconv.Itoa(linodeId)) 94 | u.Add("Hostname", hostname) 95 | v := LinodeRDNSIPAddressResponse{} 96 | if err := t.client.do("linode.ip.setrdns", u, &v.Response); err != nil { 97 | return nil, err 98 | } 99 | 100 | if err := json.Unmarshal(v.RawData, &v.RDNSIPAddress); err != nil { 101 | return nil, err 102 | } 103 | return &v, nil 104 | } 105 | 106 | // Swap Ips 107 | func (t *LinodeIPService) Swap(ipAddressId int, withIPAddressId int, toLinodeId int) (*LinodeLinodeIPAddressResponse, error) { 108 | u := &url.Values{} 109 | u.Add("toLinodeID", strconv.Itoa(toLinodeId)) 110 | u.Add("ipAddressID", strconv.Itoa(ipAddressId)) 111 | u.Add("withIPAddressID", strconv.Itoa(withIPAddressId)) 112 | v := LinodeLinodeIPAddressResponse{} 113 | if err := t.client.do("linode.ip.swap", u, &v.Response); err != nil { 114 | return nil, err 115 | } 116 | 117 | v.LinodeIPAddresses = make([]LinodeIPAddress, 2) 118 | if err := json.Unmarshal(v.RawData, &v.LinodeIPAddresses); err != nil { 119 | return nil, err 120 | } 121 | return &v, nil 122 | } 123 | -------------------------------------------------------------------------------- /linode_ip_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | -------------------------------------------------------------------------------- /linode_job_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | "strconv" 7 | ) 8 | 9 | // Job service 10 | type LinodeJobService struct { 11 | client *Client 12 | } 13 | 14 | // Resonse for linode.job.list API 15 | type LinodesJobListResponse struct { 16 | Response 17 | Jobs []Job 18 | } 19 | 20 | // List all jobs. If jobId is greater than 0, limit the list to given jobId. 21 | func (t *LinodeJobService) List(linodeId int, jobId int, pendingOnly bool) (*LinodesJobListResponse, error) { 22 | u := &url.Values{} 23 | u.Add("LinodeID", strconv.Itoa(linodeId)) 24 | if pendingOnly { 25 | u.Add("pendingOnly", "1") 26 | } 27 | if jobId > 0 { 28 | u.Add("JobID", strconv.Itoa(jobId)) 29 | } 30 | v := LinodesJobListResponse{} 31 | if err := t.client.do("linode.job.list", u, &v.Response); err != nil { 32 | return nil, err 33 | } 34 | 35 | v.Jobs = make([]Job, 5) 36 | if err := json.Unmarshal(v.RawData, &v.Jobs); err != nil { 37 | return nil, err 38 | } 39 | return &v, nil 40 | } 41 | -------------------------------------------------------------------------------- /linode_job_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | _ "github.com/sirupsen/logrus" 5 | "testing" 6 | ) 7 | 8 | func TestListJobs(t *testing.T) { 9 | client := NewClient(APIKey, nil) 10 | 11 | _, err := client.Job.List(-1, -1, false) 12 | if err == nil { 13 | t.Fatal("Should not find this job with ID -1!") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /linode_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strconv" 8 | ) 9 | 10 | // Linode Service 11 | type LinodeService struct { 12 | client *Client 13 | } 14 | 15 | // Response for linode.list API 16 | type LinodesListResponse struct { 17 | Response 18 | Linodes []Linode 19 | } 20 | 21 | // General Linode API Response 22 | type LinodeResponse struct { 23 | Response 24 | LinodeId LinodeId 25 | } 26 | 27 | // Job Response 28 | type JobResponse struct { 29 | Response 30 | JobId JobId 31 | } 32 | 33 | // List all Linodes. If linodeId is less than 0, all linodes are returned. 34 | // Otherwise, only returns the linode for given Id. 35 | func (t *LinodeService) List(linodeId int) (*LinodesListResponse, error) { 36 | u := &url.Values{} 37 | v := LinodesListResponse{} 38 | if linodeId > 0 { 39 | u.Add("LinodeID", strconv.Itoa(linodeId)) 40 | } 41 | if err := t.client.do("linode.list", u, &v.Response); err != nil { 42 | return nil, err 43 | } 44 | 45 | v.Linodes = make([]Linode, 5) 46 | if err := json.Unmarshal(v.RawData, &v.Linodes); err != nil { 47 | return nil, err 48 | } 49 | return &v, nil 50 | } 51 | 52 | // Create Linode 53 | func (t *LinodeService) Create(dataCenterId int, planId int, paymentTerm int) (*LinodeResponse, error) { 54 | u := &url.Values{} 55 | u.Add("DatacenterID", strconv.Itoa(dataCenterId)) 56 | u.Add("PlanID", strconv.Itoa(planId)) 57 | u.Add("PaymentTerm", strconv.Itoa(paymentTerm)) 58 | v := LinodeResponse{} 59 | if err := t.client.do("linode.create", u, &v.Response); err != nil { 60 | return nil, err 61 | } 62 | 63 | if err := json.Unmarshal(v.RawData, &v.LinodeId); err != nil { 64 | return nil, err 65 | } 66 | return &v, nil 67 | } 68 | 69 | // Shutdown Linode 70 | func (t *LinodeService) Shutdown(linodeId int) (*JobResponse, error) { 71 | u := &url.Values{} 72 | u.Add("LinodeID", strconv.Itoa(linodeId)) 73 | v := JobResponse{} 74 | if err := t.client.do("linode.shutdown", u, &v.Response); err != nil { 75 | return nil, err 76 | } 77 | 78 | if err := json.Unmarshal(v.RawData, &v.JobId); err != nil { 79 | return nil, err 80 | } 81 | return &v, nil 82 | } 83 | 84 | // Reboot Linode 85 | func (t *LinodeService) Reboot(linodeId int, configId int) (*JobResponse, error) { 86 | u := &url.Values{} 87 | u.Add("LinodeID", strconv.Itoa(linodeId)) 88 | if configId > 0 { 89 | u.Add("ConfigID", strconv.Itoa(configId)) 90 | } 91 | v := JobResponse{} 92 | if err := t.client.do("linode.reboot", u, &v.Response); err != nil { 93 | return nil, err 94 | } 95 | 96 | if err := json.Unmarshal(v.RawData, &v.JobId); err != nil { 97 | return nil, err 98 | } 99 | return &v, nil 100 | } 101 | 102 | // Boot Linode 103 | func (t *LinodeService) Boot(linodeId int, configId int) (*JobResponse, error) { 104 | u := &url.Values{} 105 | u.Add("LinodeID", strconv.Itoa(linodeId)) 106 | if configId > 0 { 107 | u.Add("ConfigID", strconv.Itoa(configId)) 108 | } 109 | v := JobResponse{} 110 | if err := t.client.do("linode.boot", u, &v.Response); err != nil { 111 | return nil, err 112 | } 113 | 114 | if err := json.Unmarshal(v.RawData, &v.JobId); err != nil { 115 | return nil, err 116 | } 117 | return &v, nil 118 | } 119 | 120 | // Clone Linode 121 | func (t *LinodeService) Clone(linodeId int, dataCenterId int, planId int, paymentTerm int) (*LinodeResponse, error) { 122 | u := &url.Values{} 123 | u.Add("LinodeID", strconv.Itoa(linodeId)) 124 | u.Add("DatacenterID", strconv.Itoa(dataCenterId)) 125 | u.Add("PlanID", strconv.Itoa(planId)) 126 | 127 | if paymentTerm > 0 { 128 | u.Add("PaymentTerm", strconv.Itoa(paymentTerm)) 129 | } 130 | v := LinodeResponse{} 131 | if err := t.client.do("linode.clone", u, &v.Response); err != nil { 132 | return nil, err 133 | } 134 | 135 | if err := json.Unmarshal(v.RawData, &v.LinodeId); err != nil { 136 | return nil, err 137 | } 138 | return &v, nil 139 | } 140 | 141 | // Delete Linode 142 | func (t *LinodeService) Delete(linodeId int, skipChecks bool) (*LinodeResponse, error) { 143 | u := &url.Values{} 144 | u.Add("LinodeID", strconv.Itoa(linodeId)) 145 | if skipChecks { 146 | u.Add("skipChecks", "1") 147 | } 148 | 149 | v := LinodeResponse{} 150 | if err := t.client.do("linode.delete", u, &v.Response); err != nil { 151 | return nil, err 152 | } 153 | 154 | if err := json.Unmarshal(v.RawData, &v.LinodeId); err != nil { 155 | return nil, err 156 | } 157 | return &v, nil 158 | } 159 | 160 | // Resize Linode 161 | func (t *LinodeService) Resize(linodeId int, planId int) (*LinodeResponse, error) { 162 | u := &url.Values{} 163 | u.Add("LinodeID", strconv.Itoa(linodeId)) 164 | u.Add("PlanID", strconv.Itoa(planId)) 165 | 166 | v := LinodeResponse{} 167 | if err := t.client.do("linode.resize", u, &v.Response); err != nil { 168 | return nil, err 169 | } 170 | 171 | v.LinodeId.LinodeId = linodeId // hardcode this to input 172 | return &v, nil 173 | } 174 | 175 | // Update Linode 176 | func (t *LinodeService) Update(linodeId int, args map[string]interface{}) (*LinodeResponse, error) { 177 | u := &url.Values{} 178 | 179 | u.Add("LinodeID", strconv.Itoa(linodeId)) 180 | // add optional parameters 181 | for k, v := range args { 182 | u.Add(k, fmt.Sprintf("%v", v)) 183 | } 184 | 185 | v := LinodeResponse{} 186 | if err := t.client.do("linode.update", u, &v.Response); err != nil { 187 | return nil, err 188 | } 189 | 190 | if err := json.Unmarshal(v.RawData, &v.LinodeId); err != nil { 191 | return nil, err 192 | } 193 | return &v, nil 194 | } 195 | -------------------------------------------------------------------------------- /linode_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "testing" 6 | ) 7 | 8 | func TestListLinodes(t *testing.T) { 9 | client := NewClient(APIKey, nil) 10 | 11 | v, err := client.Linode.List(-1) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | for _, linode := range v.Linodes { 17 | log.Debugf("Linode: %s, %s, %d", linode.Label, linode.CreateDt, linode.PlanId) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /linodego.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "runtime" 13 | "strings" 14 | 15 | log "github.com/sirupsen/logrus" 16 | ) 17 | 18 | const ( 19 | // API Client Version 20 | ClientVersion = "0.1" 21 | // Default Base URL 22 | defaultBaseURL = "https://api.linode.com" 23 | ) 24 | 25 | var ( 26 | // Unexpected Response Error 27 | ErrUnexpectedResponse = errors.New("Unexpected response") 28 | // General Response Error 29 | ErrResponse = errors.New("Error response") 30 | // Authentication Failure 31 | ErrAuthentication = errors.New("Authenticaion Failed. Be sure to use correct API Key") 32 | ) 33 | 34 | // Client of Linode v1 API 35 | type Client struct { 36 | // Linode API Key 37 | ApiKey string 38 | // HTTP client to communicate with Linode API 39 | HTTPClient *http.Client 40 | // Base URL 41 | BaseURL *url.URL 42 | 43 | // Whether to use POST for API request, default is false 44 | UsePost bool 45 | 46 | // Services 47 | Test *TestService 48 | Api *ApiService 49 | Avail *AvailService 50 | Account *AccountService 51 | Image *ImageService 52 | Linode *LinodeService 53 | Job *LinodeJobService 54 | Config *LinodeConfigService 55 | Ip *LinodeIPService 56 | Disk *LinodeDiskService 57 | } 58 | 59 | // Creates a new Linode client object. 60 | func NewClient(AccessKey string, httpClient *http.Client) *Client { 61 | if httpClient == nil { 62 | httpClient = http.DefaultClient 63 | } 64 | baseURL, _ := url.Parse(defaultBaseURL) 65 | 66 | if os.Getenv("DEBUG") == "true" { 67 | log.SetLevel(log.DebugLevel) // set debug level 68 | } 69 | 70 | c := &Client{ApiKey: AccessKey, HTTPClient: httpClient, BaseURL: baseURL} 71 | c.Test = &TestService{client: c} 72 | c.Api = &ApiService{client: c} 73 | c.Avail = &AvailService{client: c} 74 | c.Account = &AccountService{client: c} 75 | c.Image = &ImageService{client: c} 76 | c.Linode = &LinodeService{client: c} 77 | c.Job = &LinodeJobService{client: c} 78 | c.Config = &LinodeConfigService{client: c} 79 | c.Ip = &LinodeIPService{client: c} 80 | c.Job = &LinodeJobService{client: c} 81 | c.Disk = &LinodeDiskService{client: c} 82 | return c 83 | } 84 | 85 | // execute request 86 | func (c *Client) do(action string, params *url.Values, v *Response) error { 87 | // https://api.linode.com/?api_key={key}&api_action=test.echo&foo=bar 88 | if params == nil { 89 | params = &url.Values{} 90 | } 91 | params.Add("api_key", c.ApiKey) 92 | params.Add("api_action", action) 93 | return c.request(params, v) 94 | } 95 | 96 | // send request via POST to Linode API. The response is stored in the value pointed to by v 97 | // Returns an error if an API error has occurred. 98 | func (c *Client) request(params *url.Values, v *Response) error { 99 | var body string 100 | if params != nil { 101 | body = params.Encode() 102 | } 103 | 104 | method := "GET" 105 | requestURL := fmt.Sprintf("%v/?%s", c.BaseURL, body) 106 | var requestBody io.Reader 107 | if c.UsePost { 108 | method = "POST" 109 | requestURL = c.BaseURL.String() 110 | requestBody = strings.NewReader(body) 111 | } 112 | request, err := http.NewRequest(method, requestURL, requestBody) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | log.Debugf("HTTP REQUEST: %s %s %s", method, requestURL, body) 118 | 119 | if c.UsePost { 120 | request.Header.Set("Content-Type", "application/x-www-form-urlencoded") 121 | } 122 | request.Header.Add("Accept", "application/json") 123 | request.Header.Add("User-Agent", "LinodeGo/"+ClientVersion+" Go/"+runtime.Version()) 124 | 125 | response, err := c.HTTPClient.Do(request) 126 | if err != nil { 127 | log.Errorf("Failed to get API response: %v", err) 128 | return err 129 | } 130 | 131 | defer response.Body.Close() 132 | 133 | responseBody, err := ioutil.ReadAll(response.Body) 134 | if err != nil { 135 | log.Errorf("Failed to parse API response: %v", err) 136 | return err 137 | } 138 | 139 | log.Debugf("HTTP RESPONSE: %s", string(responseBody)) 140 | 141 | // Status code 500 is a server error and means nothing can be done at this point. 142 | if response.StatusCode != 200 { 143 | return ErrUnexpectedResponse 144 | } 145 | 146 | if err = json.Unmarshal(responseBody, v); err != nil { 147 | return err 148 | } 149 | 150 | if len(v.Errors) > 0 { 151 | if v.Errors[0].ErrorCode == 4 { 152 | return ErrAuthentication 153 | } 154 | // Need to return all errors to client. Perhaps wrap the error structure as a property for a custom error class 155 | return errors.New(fmt.Sprintf("API Error: %s, Code %d", v.Errors[0].ErrorMessage, v.Errors[0].ErrorCode)) 156 | } 157 | 158 | return nil 159 | } 160 | -------------------------------------------------------------------------------- /linodego_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | ) 7 | 8 | func TestApiRequest(t *testing.T) { 9 | client := NewClient(APIKey, nil) 10 | u := &url.Values{} 11 | u.Add("foo", "bar") 12 | v := &Response{} 13 | if err := client.do("test.echo", u, v); err != nil { 14 | t.Fatal(err) 15 | } 16 | } 17 | 18 | func TestErrorApiRequest(t *testing.T) { 19 | client := NewClient(APIKey, nil) 20 | u := &url.Values{} 21 | v := &Response{} 22 | err := client.do("test.non_implemented_method", u, v) 23 | if err == nil { 24 | t.Fatal("Should not succeed in executing method not implemented") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test_service.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | "net/url" 6 | ) 7 | 8 | // Test Service 9 | type TestService struct { 10 | client *Client 11 | } 12 | 13 | // Test Service Response 14 | type TestResponse struct { 15 | Response 16 | Data map[string]string 17 | } 18 | 19 | // Echo request with the given key and value 20 | func (t *TestService) Echo(key string, val string, v *TestResponse) error { 21 | u := &url.Values{} 22 | u.Add(key, val) 23 | if err := t.client.do("test.echo", u, &v.Response); err != nil { 24 | return err 25 | } 26 | v.Data = map[string]string{} 27 | if err := json.Unmarshal(v.RawData, &v.Data); err != nil { 28 | return err 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /test_service_test.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestEcho(t *testing.T) { 8 | client := NewClient(APIKey, nil) 9 | v := &TestResponse{} 10 | if err := client.Test.Echo("foo", "bar", v); err != nil { 11 | t.Fatal(err) 12 | } 13 | if v.Data["foo"] != "bar" { 14 | t.Fatalf("Expected bar, got %s", v.Data["foo"]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /time.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "time" 6 | ) 7 | 8 | type CustomTime struct { 9 | time.Time 10 | } 11 | 12 | const ctLayout = "2006-01-02 15:04:05.0" 13 | 14 | func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) { 15 | if b[0] == '"' && b[len(b)-1] == '"' { 16 | b = b[1 : len(b)-1] 17 | } 18 | if len(b) == 0 { 19 | return 20 | } 21 | loc, _ := time.LoadLocation("America/New_York") 22 | ct.Time, err = time.ParseInLocation(ctLayout, string(b), loc) 23 | return 24 | } 25 | 26 | func (ct *CustomTime) MarshalJSON() ([]byte, error) { 27 | return []byte(ct.Time.Format(ctLayout)), nil 28 | } 29 | 30 | var nilTime = (time.Time{}).UnixNano() 31 | 32 | func (ct *CustomTime) IsSet() bool { 33 | return ct.UnixNano() != nilTime 34 | } 35 | 36 | type CustomShortTime struct { 37 | time.Time 38 | } 39 | 40 | const ctLayout2 = "2006-01-02 15:04:05" 41 | 42 | func (ct *CustomShortTime) UnmarshalJSON(b []byte) (err error) { 43 | if b[0] == '"' && b[len(b)-1] == '"' { 44 | b = b[1 : len(b)-1] 45 | } 46 | if len(b) == 0 { 47 | return 48 | } 49 | loc, _ := time.LoadLocation("America/New_York") 50 | ct.Time, err = time.ParseInLocation(ctLayout2, string(b), loc) 51 | return 52 | } 53 | 54 | func (ct *CustomShortTime) MarshalJSON() ([]byte, error) { 55 | return []byte(ct.Time.Format(ctLayout2)), nil 56 | } 57 | 58 | func (ct *CustomShortTime) IsSet() bool { 59 | return ct.UnixNano() != nilTime 60 | } 61 | 62 | // expose a means to wait for a linode's pending jobs to complete 63 | func WaitForPendingJobs(client *Client, linodeid int) { 64 | for { 65 | jobListResp, err := client.Job.List(linodeid, 0, true) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | if len(jobListResp.Jobs) == 0 { 70 | break 71 | } 72 | time.Sleep(time.Second * 5) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package linodego 2 | 3 | import ( 4 | "encoding/json" 5 | _ "fmt" 6 | ) 7 | 8 | type Response struct { 9 | Errors []Error `json:"ERRORARRAY"` 10 | RawData json.RawMessage `json:"DATA"` 11 | Action string `json:"ACTION"` 12 | } 13 | 14 | type Error struct { 15 | ErrorCode int `json:"ERRORCODE"` 16 | ErrorMessage string `json:"ERRORMESSAGE"` 17 | } 18 | 19 | type DataCenter struct { 20 | DataCenterId int `json:"DATACENTERID"` 21 | Location string `json:"LOCATION"` 22 | Abbr string `json:"ABBR"` 23 | } 24 | 25 | type Distribution struct { 26 | Is64Bit int `json:"IS64BIT"` 27 | Label CustomString `json:"LABEL"` 28 | MinImageSize int `json:"MINIMAGESIZE"` 29 | DistributionId int `json:"DISTRIBUTIONID"` 30 | CreatedDt CustomTime `json:"CREATE_DT"` 31 | RequiresPVOPSKernel int `json:"REQUIRESPVOPSKERNEL"` 32 | } 33 | 34 | type Kernel struct { 35 | Label CustomString `json:"LABEL"` 36 | IsXen int `json:"ISXEN"` 37 | IsPVOPS int `json:"ISPVOPS"` 38 | KernelId int `json:"KERNELID"` 39 | } 40 | 41 | type LinodePlan struct { 42 | Cores int `json:"CORES"` 43 | Price float32 `json:"PRICE"` 44 | RAM int `json:"RAM"` 45 | Xfer int `json:"Xfer"` 46 | PlanId int `json:"PLANID"` 47 | Label CustomString `json:"LABEL"` 48 | Avail map[string]int `json:"AVAIL"` 49 | Disk int `json:"DISK"` 50 | Hourly float32 `json:"HOURLY"` 51 | } 52 | 53 | type NodeBalancer struct { 54 | Hourly float32 `json:"HOURLY"` 55 | Monthly float32 `json:"MONTHLY"` 56 | Connections int `json:"CONNECTIONS"` 57 | } 58 | 59 | type StackScript struct { 60 | //Script string `json:"SCRIPT"` 61 | //Description string `json:"DESCRIPTION"` 62 | //DistributionidList int `json:"DISTRIBUTIONIDLIST"` 63 | Label CustomString `json:"LABEL"` 64 | DeploymentsTotal int `json:"DEPLOYMENTSTOTAL"` 65 | LatestRev int `json:"LATESTREV"` 66 | CreatedDt CustomTime `json:"CREATE_DT"` 67 | DeploymentsActive int `json:"DEPLOYMENTSACTIVE"` 68 | StackScriptId int `json:"STACKSCRIPTID"` 69 | RevNote int `json:"REV_NOTE"` 70 | RevDt int `json:"REV_DT"` 71 | IsPublic int `json:"ISPUBLIC"` 72 | UserId int `json:"USERID"` 73 | } 74 | 75 | type EstimateInvoice struct { 76 | InvoiceTo CustomShortTime `json:"INVOICE_TO"` 77 | Amount float32 `json:"AMOUNT"` 78 | } 79 | 80 | type AccountInfo struct { 81 | AccountSince CustomTime `json:"ACTIVE_SINCE"` 82 | TransferPool int `json:"TRANSFER_POOL"` 83 | TransferUsed int `json:"TRANSFER_USED"` 84 | TransferBillable int `json:"TRANSFER_BILLABLE"` 85 | BillingMethod string `json:"BILLING_METHOD"` 86 | Managed bool `json:"MANAGED"` 87 | Balance float32 `json:"BALANCE"` 88 | } 89 | 90 | type Image struct { 91 | CreateDt CustomTime `json:"CREATE_DT"` 92 | Creator string `json:"CREATOR"` 93 | Description string `json:"DESCRIPTION"` 94 | FsType string `json:"FS_TYPE"` 95 | ImageId int `json:"IMAGEID"` 96 | IsPublic int `json:"ISPUBLIC"` 97 | Label CustomString `json:"LABEL"` 98 | LastUsedDt CustomTime `json:"LAST_USED_DT"` 99 | MinSize int `json:"MINSIZE"` 100 | Status string `json:"STATUS"` 101 | Type string `json:"TYPE"` 102 | } 103 | 104 | type Linode struct { 105 | TotalXFer int `json:"TOTALXFER"` 106 | BackupsEnabled int `json:"BACKUPSENABLED"` 107 | WatchDog int `json:"WATCHDOG"` 108 | LpmDisplayGroup string `json:"LPM_DISPLAYGROUP"` 109 | AlertBwQuotaEnabled int `json:"ALERT_BWQUOTA_ENABLED"` 110 | Status int `json:"STATUS"` 111 | TotalRAM int `json:"TOTALRAM"` 112 | AlertDiskIOThreshold int `json:"ALERT_DISKIO_THRESHOLD"` 113 | BackupWindow int `json:"BACKUPWINDOW"` 114 | AlertBwOutEnabled int `json:"ALERT_BWOUT_ENABLED"` 115 | AlertBwOutThreshold int `json:"ALERT_BWOUT_THRESHOLD"` 116 | Label CustomString `json:"LABEL"` 117 | AlertCPUEnabled int `json:"ALERT_CPU_ENABLED"` 118 | AlertBwQuotaThreshold int `json:"ALERT_BWQUOTA_THRESHOLD"` 119 | AlertBwInThreshold int `json:"ALERT_BWIN_THRESHOLD"` 120 | BackupWeeklyDay int `json:"BACKUPWEEKLYDAY"` 121 | DataCenterId int `json:"DATACENTERID"` 122 | AlertCPUThreshold int `json:"ALERT_CPU_THRESHOLD"` 123 | TotalHD int `json:"TOTALHD"` 124 | AlertDiskIOEnabled int `json:"ALERT_DISKIO_ENABLED"` 125 | AlertBwInEnabled int `json:"ALERT_BWIN_ENABLED"` 126 | LinodeId int `json:"LINODEID"` 127 | CreateDt CustomTime `json:"CREATE_DT"` 128 | PlanId int `json:"PLANID"` 129 | DistributionVendor string `json:"DISTRIBUTIONVENDOR"` 130 | } 131 | 132 | type LinodeId struct { 133 | LinodeId int `json:"LinodeID"` 134 | } 135 | 136 | type Job struct { 137 | EnteredDt CustomTime `json:"ENTERED_DT"` 138 | Action string `json:"ACTION"` 139 | Label string `json:"LABEL"` 140 | HostStartDt CustomTime `json:"HOST_START_DT"` 141 | LinodeId int `json:"LINODEID"` 142 | HostFinishDt CustomTime `json:"HOST_FINISH_DT"` 143 | HostMessage string `json:"HOST_MESSAGE"` 144 | JobId int `json:"JOBID"` 145 | HostSuccess CustomString `json:"HOST_SUCCESS"` // Linode API returns empty string if HostSuccess is false. 1 otherwise. 146 | } 147 | 148 | type LinodeConfig struct { 149 | HelperDisableUpdateDB int `json:"helper_disableUpdateDB"` 150 | RootDeviceRO bool `json:"RootDeviceRO"` 151 | RootDeviceCustom string `json:"RootDeviceCustom"` 152 | Label CustomString `json:"Label"` 153 | DiskList string `json:"DiskList"` 154 | LinodeId int `json:"LinodeID"` 155 | Comments string `json:"Comments"` 156 | ConfigId int `json:"ConfigID"` 157 | HelperXen int `json:"helper_xen"` // Depreciated, use HelperDistro instead 158 | HelperDistro CustomBool `json:"helper_distro"` 159 | RunLevel string `json:"RunLevel"` 160 | HelperDepmod CustomBool `json:"helper_depmod"` 161 | KernelId int `json:"KernelID"` 162 | RootDeviceNum int `json:"RootDeviceNum"` 163 | HelperLibtls CustomBool `json:"helper_libtls"` 164 | HelperNetwork CustomBool `json:"helper_network"` 165 | RAMLimit int `json:"RAMLimit"` 166 | } 167 | 168 | type LinodeConfigId struct { 169 | LinodeConfigId int `json:"ConfigID"` 170 | } 171 | 172 | type JobId struct { 173 | JobId int `json:"JobID"` 174 | } 175 | 176 | type DiskJob struct { 177 | JobId int `json:"JobID"` 178 | DiskId int `json:"DiskID"` 179 | } 180 | 181 | type Disk struct { 182 | UpdateDt CustomTime `json:"UPDATE_DT"` 183 | DiskId int `json:"DISKID"` 184 | Label CustomString `json:"LABEL"` 185 | Type string `json:"TYPE"` 186 | LinodeId int `json:"LINODEID"` 187 | IsReadOnly int `json:"ISREADONLY"` 188 | Status int `json:"STATUS"` 189 | CreateDt CustomTime `json:"CREATE_DT"` 190 | Size int `json:"SIZE"` 191 | } 192 | 193 | type IPAddress struct { 194 | IPAddress string `json:"IPAddress"` 195 | IPAddressId int `json:"IPAddressID"` 196 | } 197 | 198 | type FullIPAddress struct { 199 | LinodeId int `json:"LINODEID"` 200 | IsPublic int `json:"ISPUBLIC"` 201 | RDNSName string `json:"RDNS_NAME"` 202 | IPAddress string `json:"IPADDRESS"` 203 | IPAddressId int `json:"IPADDRESSID"` 204 | } 205 | 206 | type RDNSIPAddress struct { 207 | HostName string `json:"HOSTNAME"` 208 | IPAddress string `json:"IPADDRESS"` 209 | IPAddressId int `json:"IPADDRESSID"` 210 | } 211 | 212 | type LinodeIPAddress struct { 213 | LinodeId int `json:"LINODEID"` 214 | IPAddress string `json:"IPADDRESS"` 215 | IPAddressId int `json:"IPADDRESSID"` 216 | } 217 | --------------------------------------------------------------------------------