func()

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
}