in generate/generate.go [326:869]
func (as *allServices) GeneralCode() ([]byte, error) {
// Buffer the output in memory, for gofmt'ing later in the defer.
var buf bytes.Buffer
p := func(format string, args ...interface{}) {
_, err := fmt.Fprintf(&buf, format, args...)
if err != nil {
panic(err)
}
}
pn := func(format string, args ...interface{}) {
p(format+"\n", args...)
}
pn("//")
pn("// Licensed to the Apache Software Foundation (ASF) under one")
pn("// or more contributor license agreements. See the NOTICE file")
pn("// distributed with this work for additional information")
pn("// regarding copyright ownership. The ASF licenses this file")
pn("// to you under the Apache License, Version 2.0 (the")
pn("// \"License\"); you may not use this file except in compliance")
pn("// with the License. You may obtain a copy of the License at")
pn("//")
pn("// http://www.apache.org/licenses/LICENSE-2.0")
pn("//")
pn("// Unless required by applicable law or agreed to in writing,")
pn("// software distributed under the License is distributed on an")
pn("// \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY")
pn("// KIND, either express or implied. See the License for the")
pn("// specific language governing permissions and limitations")
pn("// under the License.")
pn("//")
pn("")
pn("package %s", pkg)
pn("")
pn("// UnlimitedResourceID is a special ID to define an unlimited resource")
pn("const UnlimitedResourceID = \"-1\"")
pn("")
pn("var idRegex = regexp.MustCompile(`^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|-1)$`)")
pn("")
pn("// IsID return true if the passed ID is either a UUID or a UnlimitedResourceID")
pn("func IsID(id string) bool {")
pn(" return idRegex.MatchString(id)")
pn("}")
pn("")
pn("// ClientOption can be passed to new client functions to set custom options")
pn("type ClientOption func(*CloudStackClient)")
pn("")
pn("// OptionFunc can be passed to the courtesy helper functions to set additional parameters")
pn("type OptionFunc func(*CloudStackClient, interface{}) error")
pn("")
pn("type CSError struct {")
pn(" ErrorCode int `json:\"errorcode\"`")
pn(" CSErrorCode int `json:\"cserrorcode\"`")
pn(" ErrorText string `json:\"errortext\"`")
pn("}")
pn("")
pn("func (e *CSError) Error() error {")
pn(" return fmt.Errorf(\"CloudStack API error %%d (CSExceptionErrorCode: %%d): %%s\", e.ErrorCode, e.CSErrorCode, e.ErrorText)")
pn("}")
pn("")
pn("type UUID string")
pn("")
pn("func (c UUID) MarshalJSON() ([]byte, error) {")
pn(" return json.Marshal(string(c))")
pn("}")
pn("")
pn("func (c *UUID) UnmarshalJSON(data []byte) error {")
pn(" value := strings.Trim(string(data), \"\\\"\")")
pn(" if strings.HasPrefix(string(data), \"\\\"\") {")
pn(" *c = UUID(value)")
pn(" return nil")
pn(" }")
pn(" _, err := strconv.ParseInt(value, 10, 64)")
pn(" if err != nil {")
pn(" return err")
pn(" }")
pn(" *c = UUID(value)")
pn(" return nil")
pn("}")
pn("type CloudStackClient struct {")
pn(" HTTPGETOnly bool // If `true` only use HTTP GET calls")
pn("")
pn(" client *http.Client // The http client for communicating")
pn(" baseURL string // The base URL of the API")
pn(" apiKey string // Api key")
pn(" secret string // Secret key")
pn(" async bool // Wait for async calls to finish")
pn(" options []OptionFunc // A list of option functions to apply to all API calls")
pn(" timeout int64 // Max waiting timeout in seconds for async jobs to finish; defaults to 300 seconds")
pn("")
for _, s := range as.services {
pn(" %s %sIface", strings.TrimSuffix(s.name, "Service"), s.name)
}
pn("}")
pn("")
pn("// Creates a new client for communicating with CloudStack")
pn("func newClient(apiurl string, apikey string, secret string, async bool, verifyssl bool, options ...ClientOption) *CloudStackClient {")
pn(" jar, _ := cookiejar.New(nil)")
pn(" cs := &CloudStackClient{")
pn(" client: &http.Client{")
pn(" Jar: jar,")
pn(" Transport: &http.Transport{")
pn(" Proxy: http.ProxyFromEnvironment,")
pn(" DialContext: (&net.Dialer{")
pn(" Timeout: 30 * time.Second,")
pn(" KeepAlive: 30 * time.Second,")
pn(" DualStack: true,")
pn(" }).DialContext,")
pn(" MaxIdleConns: 100,")
pn(" IdleConnTimeout: 90 * time.Second,")
pn(" TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifyssl},")
pn(" TLSHandshakeTimeout: 10 * time.Second,")
pn(" ExpectContinueTimeout: 1 * time.Second,")
pn(" },")
pn(" Timeout: time.Duration(60 * time.Second),")
pn(" },")
pn(" baseURL: apiurl,")
pn(" apiKey: apikey,")
pn(" secret: secret,")
pn(" async: async,")
pn(" options: []OptionFunc{},")
pn(" timeout: 300,")
pn(" }")
pn("")
pn(" for _, fn := range options {")
pn(" fn(cs)")
pn(" }")
pn("")
for _, s := range as.services {
pn(" cs.%s = New%s(cs)", strings.TrimSuffix(s.name, "Service"), s.name)
}
pn("")
pn(" return cs")
pn("}")
pn("")
pn("// Creates a new mock client for communicating with CloudStack")
pn("func newMockClient(ctrl *gomock.Controller) *CloudStackClient {")
pn(" cs := &CloudStackClient{}")
pn("")
for _, s := range as.services {
pn(" cs.%s = NewMock%sIface(ctrl)", strings.TrimSuffix(s.name, "Service"), s.name)
}
pn("")
pn(" return cs")
pn("}")
pn("")
pn("// Default non-async client. So for async calls you need to implement and check the async job result yourself. When using")
pn("// HTTPS with a self-signed certificate to connect to your CloudStack API, you would probably want to set 'verifyssl' to")
pn("// false so the call ignores the SSL errors/warnings.")
pn("func NewClient(apiurl string, apikey string, secret string, verifyssl bool, options ...ClientOption) *CloudStackClient {")
pn(" cs := newClient(apiurl, apikey, secret, false, verifyssl, options...)")
pn(" return cs")
pn("}")
pn("")
pn("// For sync API calls this client behaves exactly the same as a standard client call, but for async API calls")
pn("// this client will wait until the async job is finished or until the configured AsyncTimeout is reached. When the async")
pn("// job finishes successfully it will return actual object received from the API and nil, but when the timeout is")
pn("// reached it will return the initial object containing the async job ID for the running job and a warning.")
pn("func NewAsyncClient(apiurl string, apikey string, secret string, verifyssl bool, options ...ClientOption) *CloudStackClient {")
pn(" cs := newClient(apiurl, apikey, secret, true, verifyssl, options...)")
pn(" return cs")
pn("}")
pn("")
pn("// Creates a new mock client for communicating with CloudStack")
pn("func NewMockClient(ctrl *gomock.Controller) *CloudStackClient {")
pn(" cs := newMockClient(ctrl)")
pn(" return cs")
pn("}")
pn("")
pn("// When using the async client an api call will wait for the async call to finish before returning. The default is to poll for 300 seconds")
pn("// seconds, to check if the async job is finished.")
pn("func (cs *CloudStackClient) AsyncTimeout(timeoutInSeconds int64) {")
pn(" cs.timeout = timeoutInSeconds")
pn("}")
pn("")
pn("// Sets timeout when using sync api calls. Default is 60 seconds")
pn("func (cs *CloudStackClient) Timeout(timeout time.Duration) {")
pn(" cs.client.Timeout = timeout")
pn("}")
pn("")
pn("// Set any default options that would be added to all API calls that support it.")
pn("func (cs *CloudStackClient) DefaultOptions(options ...OptionFunc) {")
pn(" if options != nil {")
pn(" cs.options = options")
pn(" } else {")
pn(" cs.options = []OptionFunc{}")
pn(" }")
pn("}")
pn("")
pn("var AsyncTimeoutErr = errors.New(\"Timeout while waiting for async job to finish\")")
pn("")
pn("// A helper function that you can use to get the result of a running async job. If the job is not finished within the configured")
pn("// timeout, the async job returns a AsyncTimeoutErr.")
pn("func (cs *CloudStackClient) GetAsyncJobResult(jobid string, timeout int64) (json.RawMessage, error) {")
pn(" var timer time.Duration")
pn(" currentTime := time.Now().Unix()")
pn("")
pn(" for {")
pn(" p := cs.Asyncjob.NewQueryAsyncJobResultParams(jobid)")
pn(" r, err := cs.Asyncjob.QueryAsyncJobResult(p)")
pn(" if err != nil {")
pn(" return nil, err")
pn(" }")
pn("")
pn(" // Status 1 means the job is finished successfully")
pn(" if r.Jobstatus == 1 {")
pn(" return r.Jobresult, nil")
pn(" }")
pn("")
pn(" // When the status is 2, the job has failed")
pn(" if r.Jobstatus == 2 {")
pn(" if r.Jobresulttype == \"text\" {")
pn(" return nil, fmt.Errorf(string(r.Jobresult))")
pn(" } else {")
pn(" return nil, fmt.Errorf(\"Undefined error: %%s\", string(r.Jobresult))")
pn(" }")
pn(" }")
pn("")
pn(" if time.Now().Unix()-currentTime > timeout {")
pn(" return nil, AsyncTimeoutErr")
pn(" }")
pn("")
pn(" // Add an (extremely simple) exponential backoff like feature to prevent")
pn(" // flooding the CloudStack API")
pn(" if timer < 15 {")
pn(" timer++")
pn(" }")
pn("")
pn(" time.Sleep(timer * time.Second)")
pn(" }")
pn("}")
pn("")
pn("// Execute the request against a CS API. Will return the raw JSON data returned by the API and nil if")
pn("// no error occurred. If the API returns an error the result will be nil and the HTTP error code and CS")
pn("// error details. If a processing (code) error occurs the result will be nil and the generated error")
pn("func (cs *CloudStackClient) newRequest(api string, params url.Values) (json.RawMessage, error) {")
pn(" return cs.newRawRequest(api, false, params)")
pn("}")
pn("")
pn("// Execute the request against a CS API using POST. Will return the raw JSON data returned by the API and")
pn("// nil if no error occurred. If the API returns an error the result will be nil and the HTTP error code")
pn("// and CS error details. If a processing (code) error occurs the result will be nil and the generated error")
pn("func (cs *CloudStackClient) newPostRequest(api string, params url.Values) (json.RawMessage, error) {")
pn(" return cs.newRawRequest(api, true, params)")
pn("}")
pn("")
pn("// Execute a raw request against a CS API. Will return the raw JSON data returned by the API and nil if")
pn("// no error occurred. If the API returns an error the result will be nil and the HTTP error code and CS")
pn("// error details. If a processing (code) error occurs the result will be nil and the generated error")
pn("func (cs *CloudStackClient) newRawRequest(api string, post bool, params url.Values) (json.RawMessage, error) {")
pn(" params.Set(\"apiKey\", cs.apiKey)")
pn(" params.Set(\"command\", api)")
pn(" params.Set(\"response\", \"json\")")
pn("")
pn(" // Generate signature for API call")
pn(" // * Serialize parameters, URL encoding only values and sort them by key, done by EncodeValues")
pn(" // * Convert the entire argument string to lowercase")
pn(" // * Replace all instances of '+' to '%%20'")
pn(" // * Calculate HMAC SHA1 of argument string with CloudStack secret")
pn(" // * URL encode the string and convert to base64")
pn(" s := EncodeValues(params)")
pn(" s2 := strings.ToLower(s)")
pn(" mac := hmac.New(sha1.New, []byte(cs.secret))")
pn(" mac.Write([]byte(s2))")
pn(" signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))")
pn("")
pn(" var err error")
pn(" var resp *http.Response")
pn(" if !cs.HTTPGETOnly && post {")
pn(" // The deployVirtualMachine API should be called using a POST call")
pn(" // so we don't have to worry about the userdata size")
pn("")
pn(" // Add the unescaped signature to the POST params")
pn(" params.Set(\"signature\", signature)")
pn("")
pn(" // Make a POST call")
pn(" resp, err = cs.client.PostForm(cs.baseURL, params)")
pn(" } else {")
pn(" // Create the final URL before we issue the request")
pn(" url := cs.baseURL + \"?\" + s + \"&signature=\" + url.QueryEscape(signature)")
pn("")
pn(" // Make a GET call")
pn(" resp, err = cs.client.Get(url)")
pn(" }")
pn(" if err != nil {")
pn(" return nil, err")
pn(" }")
pn(" defer resp.Body.Close()")
pn("")
pn(" b, err := ioutil.ReadAll(resp.Body)")
pn(" if err != nil {")
pn(" return nil, err")
pn(" }")
pn("")
pn(" // Need to get the raw value to make the result play nice")
pn(" b, err = getRawValue(b)")
pn(" if err != nil {")
pn(" return nil, err")
pn(" }")
pn("")
pn(" if resp.StatusCode != 200 {")
pn(" var e CSError")
pn(" if err := json.Unmarshal(b, &e); err != nil {")
pn(" return nil, err")
pn(" }")
pn(" return nil, e.Error()")
pn(" }")
pn(" return b, nil")
pn("}")
pn("")
pn("// Custom version of net/url Encode that only URL escapes values")
pn("// Unmodified portions here remain under BSD license of The Go Authors: https://go.googlesource.com/go/+/master/LICENSE")
pn("func EncodeValues(v url.Values) string {")
pn(" if v == nil {")
pn(" return \"\"")
pn(" }")
pn(" var buf bytes.Buffer")
pn(" keys := make([]string, 0, len(v))")
pn(" for k := range v {")
pn(" keys = append(keys, k)")
pn(" }")
pn(" sort.Strings(keys)")
pn(" for _, k := range keys {")
pn(" vs := v[k]")
pn(" prefix := k + \"=\"")
pn(" for _, v := range vs {")
pn(" if buf.Len() > 0 {")
pn(" buf.WriteByte('&')")
pn(" }")
pn(" buf.WriteString(prefix)")
pn(" escaped := url.QueryEscape(v)")
pn(" // we need to ensure + (representing a space) is encoded as %%20")
pn(" escaped = strings.Replace(escaped, \"+\", \"%%20\", -1)")
pn(" // we need to ensure * is not escaped")
pn(" escaped = strings.Replace(escaped, \"%%2A\", \"*\", -1)")
pn(" buf.WriteString(escaped)")
pn(" }")
pn(" }")
pn(" return buf.String()")
pn("}")
pn("")
pn("// Generic function to get the first non-count raw value from a response as json.RawMessage")
pn("func getRawValue(b json.RawMessage) (json.RawMessage, error) {")
pn(" var m map[string]json.RawMessage")
pn(" if err := json.Unmarshal(b, &m); err != nil {")
pn(" return nil, err")
pn(" }")
pn(" getArrayResponse := false")
pn(" for k := range m {")
pn(" if k == \"count\" {")
pn(" getArrayResponse = true")
pn(" }")
pn(" }")
pn(" if getArrayResponse {")
pn(" var resp []json.RawMessage")
pn(" for k, v := range m {")
pn(" if k != \"count\" {")
pn(" if err := json.Unmarshal(v, &resp); err != nil {")
pn(" return nil, err")
pn(" }")
pn(" return resp[0], nil")
pn(" }")
pn(" }")
pn("")
pn(" } else {")
pn(" for _, v := range m {")
pn(" return v, nil")
pn(" }")
pn(" }")
pn(" return nil, fmt.Errorf(\"Unable to extract the raw value from:\\n\\n%%s\\n\\n\", string(b))")
pn("}")
pn("")
pn("// getSortedKeysFromMap returns the keys from m in increasing order.")
pn("func getSortedKeysFromMap(m map[string]string) (keys []string) {")
pn(" for k := range m {")
pn(" keys = append(keys, k)")
pn(" }")
pn(" sort.Strings(keys)")
pn(" return keys")
pn("}")
pn("")
pn("// WithAsyncTimeout takes a custom timeout to be used by the CloudStackClient")
pn("func WithAsyncTimeout(timeout int64) ClientOption {")
pn(" return func(cs *CloudStackClient) {")
pn(" if timeout != 0 {")
pn(" cs.timeout = timeout")
pn(" }")
pn(" }")
pn("}")
pn("")
pn("// DomainIDSetter is an interface that every type that can set a domain ID must implement")
pn("type DomainIDSetter interface {")
pn(" SetDomainid(string)")
pn("}")
pn("")
pn("// WithDomain takes either a domain name or ID and sets the `domainid` parameter")
pn("func WithDomain(domain string) OptionFunc {")
pn(" return func(cs *CloudStackClient, p interface{}) error {")
pn(" ps, ok := p.(DomainIDSetter)")
pn("")
pn(" if !ok || domain == \"\" {")
pn(" return nil")
pn(" }")
pn("")
pn(" if !IsID(domain) {")
pn(" id, _, err := cs.Domain.GetDomainID(domain)")
pn(" if err != nil {")
pn(" return err")
pn(" }")
pn(" domain = id")
pn(" }")
pn("")
pn(" ps.SetDomainid(domain)")
pn("")
pn(" return nil")
pn(" }")
pn("}")
pn("")
pn("// WithHTTPClient takes a custom HTTP client to be used by the CloudStackClient")
pn("func WithHTTPClient(client *http.Client) ClientOption {")
pn(" return func(cs *CloudStackClient) {")
pn(" if client != nil {")
pn(" if client.Jar == nil {")
pn(" client.Jar = cs.client.Jar")
pn(" }")
pn(" cs.client = client")
pn(" }")
pn(" }")
pn("}")
pn("")
pn("// ListallSetter is an interface that every type that can set listall must implement")
pn("type ListallSetter interface {")
pn(" SetListall(bool)")
pn("}")
pn("")
pn("// WithListall takes either a project name or ID and sets the `listall` parameter")
pn("func WithListall(listall bool) OptionFunc {")
pn(" return func(cs *CloudStackClient, p interface{}) error {")
pn(" ps, ok := p.(ListallSetter)")
pn("")
pn(" if !ok {")
pn(" return nil")
pn(" }")
pn("")
pn(" ps.SetListall(listall)")
pn("")
pn(" return nil")
pn(" }")
pn("}")
pn("// ProjectIDSetter is an interface that every type that can set a project ID must implement")
pn("type ProjectIDSetter interface {")
pn(" SetProjectid(string)")
pn("}")
pn("")
pn("// WithProject takes either a project name or ID and sets the `projectid` parameter")
pn("func WithProject(project string) OptionFunc {")
pn(" return func(cs *CloudStackClient, p interface{}) error {")
pn(" ps, ok := p.(ProjectIDSetter)")
pn("")
pn(" if !ok || project == \"\" {")
pn(" return nil")
pn(" }")
pn("")
pn(" if !IsID(project) {")
pn(" id, _, err := cs.Project.GetProjectID(project)")
pn(" if err != nil {")
pn(" return err")
pn(" }")
pn(" project = id")
pn(" }")
pn("")
pn(" ps.SetProjectid(project)")
pn("")
pn(" return nil")
pn(" }")
pn("}")
pn("")
pn("// VPCIDSetter is an interface that every type that can set a vpc ID must implement")
pn("type VPCIDSetter interface {")
pn(" SetVpcid(string)")
pn("}")
pn("")
pn("// WithVPCID takes a vpc ID and sets the `vpcid` parameter")
pn("func WithVPCID(id string) OptionFunc {")
pn(" return func(cs *CloudStackClient, p interface{}) error {")
pn(" vs, ok := p.(VPCIDSetter)")
pn("")
pn(" if !ok || id == \"\" {")
pn(" return nil")
pn(" }")
pn("")
pn(" vs.SetVpcid(id)")
pn("")
pn(" return nil")
pn(" }")
pn("}")
pn("")
pn("// ZoneIDSetter is an interface that every type that can set a zone ID must implement")
pn("type ZoneIDSetter interface {")
pn(" SetZoneid(string)")
pn("}")
pn("")
pn("// WithZone takes either a zone name or ID and sets the `zoneid` parameter")
pn("func WithZone(zone string) OptionFunc {")
pn(" return func(cs *CloudStackClient, p interface{}) error {")
pn(" zs, ok := p.(ZoneIDSetter)")
pn("")
pn(" if !ok || zone == \"\" {")
pn(" return nil")
pn(" }")
pn("")
pn(" if !IsID(zone) {")
pn(" id, _, err := cs.Zone.GetZoneID(zone)")
pn(" if err != nil {")
pn(" return err")
pn(" }")
pn(" zone = id")
pn(" }")
pn("")
pn(" zs.SetZoneid(zone)")
pn("")
pn(" return nil")
pn(" }")
pn("}")
pn("")
for _, s := range as.services {
pn("type %s struct {", s.name)
pn(" cs *CloudStackClient")
pn("}")
pn("")
pn("func New%s(cs *CloudStackClient) %sIface {", s.name, s.name)
pn(" return &%s{cs: cs}", s.name)
pn("}")
pn("")
}
clean, err := format.Source(buf.Bytes())
if err != nil {
return buf.Bytes(), err
}
return clean, err
}