github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/cli/client/client.go (about)

     1  // Copyright (C) 2015 NTT Innovation Institute, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package client
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"strings"
    25  	"text/template"
    26  
    27  	"github.com/rackspace/gophercloud"
    28  	"github.com/rackspace/gophercloud/openstack"
    29  	"github.com/rackspace/gophercloud/openstack/common/extensions"
    30  
    31  	l "github.com/cloudwan/gohan/log"
    32  	"github.com/cloudwan/gohan/schema"
    33  )
    34  
    35  var (
    36  	log                 = l.NewLoggerForModule("gohan.cli.client")
    37  	logOutput io.Writer = os.Stderr
    38  )
    39  
    40  const schemaListTemplate = `gohan client {{.ID}} # {{.Title}}
    41  `
    42  
    43  const schemaTemplate = `
    44    {{.Title}}
    45    -----------------------------------
    46    Description: {{.Description}}
    47  
    48    Properties: {{$properties := .JSONSchema.properties}}
    49    {{range $key := .JSONSchema.propertiesOrder}} - {{$key}} {{with index $properties $key}}[ {{.type}} ]: {{.title}} {{.description}}{{end}}
    50    {{end}}
    51  
    52    Commands:
    53  
    54    - List all {{.Title}} resources
    55  
    56      gohan client {{.ID}} list
    57  
    58    - Show a {{.Title}} resources
    59  
    60      gohan client {{.ID}} show [ID]
    61  
    62    - Create a {{.Title}} resources
    63  
    64      gohan client {{.ID}} create \
    65  {{$propertiesOnCreate := .JSONSchemaOnCreate.properties}}{{range $key := .JSONSchema.propertiesOrder}}{{with index $propertiesOnCreate $key}}      --{{$key}} [ {{.type}} ] \
    66  {{end}}{{end}}
    67  
    68    - Update {{.Title}} resources
    69  
    70      gohan client {{.ID}} set \
    71  {{$propertiesOnUpdate := .JSONSchemaOnUpdate.properties}}{{range $key := .JSONSchema.propertiesOrder}}{{with index $propertiesOnUpdate $key}}      --{{$key}} [{{.type}} ] \
    72  {{end}}{{end}}       [ID]
    73  
    74    - Delete {{.Title}} resources
    75  
    76    gohan client {{.ID}} delete [ID]
    77  
    78  `
    79  
    80  // GohanClientCLI ...
    81  type GohanClientCLI struct {
    82  	provider *gophercloud.ProviderClient
    83  	schemas  []*schema.Schema
    84  	commands []gohanCommand
    85  	opts     *GohanClientCLIOpts
    86  }
    87  
    88  func setUpLogging(level l.Level) {
    89  	l.SetUpBasicLogging(logOutput, l.CliFormat, "gohan.cli.client", level)
    90  }
    91  
    92  // ExecuteCommand ...
    93  func (gohanClientCLI *GohanClientCLI) ExecuteCommand(command string, arguments []string) (string, error) {
    94  	for _, c := range gohanClientCLI.commands {
    95  		if c.Name == command {
    96  			return c.Action(arguments)
    97  		}
    98  	}
    99  	return gohanClientCLI.outputSubCommands(command)
   100  }
   101  
   102  //Output sub command helps
   103  func (gohanClientCLI *GohanClientCLI) outputSubCommands(command string) (string, error) {
   104  	schemas, err := gohanClientCLI.getSchemas()
   105  	command = strings.TrimSpace(command)
   106  	buf := new(bytes.Buffer)
   107  	if err != nil {
   108  		return "", err
   109  	}
   110  	//Output schema specific help
   111  	for _, schema := range schemas {
   112  		if command == schema.ID {
   113  			tmpl, _ := template.New("schema").Parse(schemaTemplate)
   114  			tmpl.Execute(buf, schema)
   115  			return buf.String(), nil
   116  		}
   117  	}
   118  
   119  	tmpl, _ := template.New("schema").Parse(schemaListTemplate)
   120  	if command != "" {
   121  		buf.WriteString("Command not found")
   122  		return buf.String(), nil
   123  	}
   124  	for _, schema := range schemas {
   125  		tmpl.Execute(buf, schema)
   126  	}
   127  	return buf.String(), nil
   128  }
   129  
   130  // NewGohanClientCLI GohanClientCLI constructor
   131  func NewGohanClientCLI(opts *GohanClientCLIOpts) (*GohanClientCLI, error) {
   132  	gohanClientCLI := GohanClientCLI{
   133  		opts: opts,
   134  	}
   135  	setUpLogging(gohanClientCLI.opts.logLevel)
   136  
   137  	provider, err := getProviderClient()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	gohanClientCLI.provider = provider
   142  
   143  	if opts.authTokenID != "" {
   144  		gohanClientCLI.provider.TokenID = opts.authTokenID
   145  	}
   146  
   147  	if opts.gohanEndpointURL == "" {
   148  		gohanEndpointURL, err := gohanClientCLI.getGohanEndpointURL(provider)
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  		gohanClientCLI.opts.gohanEndpointURL = gohanEndpointURL
   153  	}
   154  
   155  	var schemas []*schema.Schema
   156  	if gohanClientCLI.opts.cacheSchemas {
   157  		schemas, err = gohanClientCLI.getCachedSchemas()
   158  	} else {
   159  		schemas, err = gohanClientCLI.getSchemas()
   160  	}
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	gohanClientCLI.schemas = schemas
   165  	if gohanClientCLI.opts.cacheSchemas {
   166  		err := gohanClientCLI.setCachedSchemas()
   167  		if err != nil {
   168  			return nil, fmt.Errorf("Error caching schemas: %v", err)
   169  		}
   170  	}
   171  
   172  	gohanClientCLI.commands = gohanClientCLI.getCommands()
   173  
   174  	return &gohanClientCLI, nil
   175  }
   176  
   177  func getProviderClient() (*gophercloud.ProviderClient, error) {
   178  	opts, err := openstack.AuthOptionsFromEnv()
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	provider, err := openstack.AuthenticatedClient(opts)
   183  	if err != nil {
   184  		if strings.Contains(err.Error(), "provide exactly one of Domain") {
   185  			return nil, fmt.Errorf(envVariablesNotSetError, keystoneDomainIDKey, keystoneDomainNameKey)
   186  		}
   187  	}
   188  	return provider, err
   189  }
   190  
   191  func (gohanClientCLI *GohanClientCLI) getGohanEndpointURL(provider *gophercloud.ProviderClient) (string, error) {
   192  	endpointOpts := gophercloud.EndpointOpts{
   193  		Type:         gohanClientCLI.opts.gohanServiceName,
   194  		Region:       gohanClientCLI.opts.gohanRegion,
   195  		Availability: gophercloud.AvailabilityAdmin,
   196  	}
   197  	endpoint, err := provider.EndpointLocator(endpointOpts)
   198  	if err != nil {
   199  		return "", err
   200  	}
   201  	return strings.TrimSuffix(endpoint, "/"), nil
   202  }
   203  
   204  func (gohanClientCLI *GohanClientCLI) getSchemas() ([]*schema.Schema, error) {
   205  	response := extensions.GetResult{}
   206  	url := fmt.Sprintf("%s%s", gohanClientCLI.opts.gohanEndpointURL, gohanClientCLI.opts.gohanSchemaURL)
   207  	gohanClientCLI.logRequest("GET", url, gohanClientCLI.provider.TokenID, nil)
   208  	_, err := gohanClientCLI.provider.Get(url, &response.Body, nil)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	bodyMap := response.Body.(map[string]interface{})
   214  	if _, ok := bodyMap["schemas"]; !ok {
   215  		return nil, fmt.Errorf("No 'schemas' key in response JSON")
   216  	}
   217  
   218  	result := []*schema.Schema{}
   219  	for _, rawSchema := range bodyMap["schemas"].([]interface{}) {
   220  		schema, err := schema.NewSchemaFromObj(rawSchema)
   221  		if err != nil {
   222  			return nil, fmt.Errorf("Could not parse schemas: %v", err)
   223  		}
   224  		result = append(result, schema)
   225  	}
   226  	return result, nil
   227  }
   228  
   229  func (gohanClientCLI *GohanClientCLI) getSchemaByID(id string) (*schema.Schema, error) {
   230  	for _, s := range gohanClientCLI.schemas {
   231  		if s.ID == id {
   232  			return s, nil
   233  		}
   234  	}
   235  	return nil, fmt.Errorf("Schema with ID '%s' not found", id)
   236  }
   237  
   238  func (gohanClientCLI *GohanClientCLI) logRequest(method, url, tokenID string, args map[string]interface{}) {
   239  	log.Notice("Sent request: %s %s", method, url)
   240  	log.Debug("X-Auth-Token: %s", tokenID)
   241  	jsonArgs, _ := json.MarshalIndent(args, "", "    ")
   242  	log.Info("Request body:\n %s", jsonArgs)
   243  }
   244  
   245  func (gohanClientCLI *GohanClientCLI) logResponse(status string, body interface{}) {
   246  	log.Notice("Received response: %s", status)
   247  	jsonBody, _ := json.MarshalIndent(body, "", "    ")
   248  	log.Info("Response body:\n %s", jsonBody)
   249  }