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 }