cyclid-client 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +174 -0
- data/README.md +587 -0
- data/bin/cyclid +20 -0
- data/lib/cyclid/auth_methods.rb +25 -0
- data/lib/cyclid/cli.rb +90 -0
- data/lib/cyclid/cli/admin.rb +32 -0
- data/lib/cyclid/cli/admin/organization.rb +122 -0
- data/lib/cyclid/cli/admin/user.rb +142 -0
- data/lib/cyclid/cli/job.rb +114 -0
- data/lib/cyclid/cli/organization.rb +129 -0
- data/lib/cyclid/cli/organization/config.rb +90 -0
- data/lib/cyclid/cli/organization/member.rb +126 -0
- data/lib/cyclid/cli/secret.rb +42 -0
- data/lib/cyclid/cli/stage.rb +121 -0
- data/lib/cyclid/cli/user.rb +84 -0
- data/lib/cyclid/client.rb +98 -0
- data/lib/cyclid/client/api.rb +114 -0
- data/lib/cyclid/client/api/basic.rb +30 -0
- data/lib/cyclid/client/api/hmac.rb +59 -0
- data/lib/cyclid/client/api/none.rb +29 -0
- data/lib/cyclid/client/api/token.rb +30 -0
- data/lib/cyclid/client/auth.rb +36 -0
- data/lib/cyclid/client/health.rb +34 -0
- data/lib/cyclid/client/job.rb +88 -0
- data/lib/cyclid/client/organization.rb +187 -0
- data/lib/cyclid/client/stage.rb +79 -0
- data/lib/cyclid/client/user.rb +134 -0
- data/lib/cyclid/config.rb +92 -0
- metadata +157 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'colorize'
|
16
|
+
|
17
|
+
# Cyclid top level module
|
18
|
+
module Cyclid
|
19
|
+
module Cli
|
20
|
+
# 'user' sub-command
|
21
|
+
class User < Thor
|
22
|
+
desc 'show', 'Show your user details'
|
23
|
+
def show
|
24
|
+
user = client.user_get(client.config.username)
|
25
|
+
|
26
|
+
# Pretty print the user details
|
27
|
+
puts 'Username: '.colorize(:cyan) + user['username']
|
28
|
+
puts 'Name: '.colorize(:cyan) + (user['name'] || '')
|
29
|
+
puts 'Email: '.colorize(:cyan) + user['email']
|
30
|
+
puts 'Organizations'.colorize(:cyan)
|
31
|
+
if user['organizations'].any?
|
32
|
+
user['organizations'].each do |org|
|
33
|
+
puts "\t#{org}"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
puts "\tNone"
|
37
|
+
end
|
38
|
+
rescue StandardError => ex
|
39
|
+
abort "Failed to get user: #{ex}"
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'modify', 'Modify your user'
|
43
|
+
long_desc <<-LONGDESC
|
44
|
+
Modify your user details.
|
45
|
+
|
46
|
+
The --email option sets your email address.
|
47
|
+
|
48
|
+
The --password option sets an encrypted password for HTTP Basic authentication and Cyclid
|
49
|
+
UI console logins.
|
50
|
+
|
51
|
+
The --secret option sets a shared secret which is used for signing Cyclid API requests.
|
52
|
+
LONGDESC
|
53
|
+
option :email, aliases: '-e'
|
54
|
+
option :password, aliases: '-p'
|
55
|
+
option :secret, aliases: '-s'
|
56
|
+
def modify
|
57
|
+
client.user_modify(client.config.username,
|
58
|
+
email: options[:email],
|
59
|
+
password: options[:password],
|
60
|
+
secret: options[:secret])
|
61
|
+
rescue StandardError => ex
|
62
|
+
abort "Failed to modify user: #{ex}"
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'passwd', 'Change your password'
|
66
|
+
def passwd
|
67
|
+
# Get the new password
|
68
|
+
print 'Password: '
|
69
|
+
password = STDIN.noecho(&:gets).chomp
|
70
|
+
print "\nConfirm password: "
|
71
|
+
confirm = STDIN.noecho(&:gets).chomp
|
72
|
+
print "\n"
|
73
|
+
abort 'Passwords do not match' unless password == confirm
|
74
|
+
|
75
|
+
# Modify the user with the new password
|
76
|
+
begin
|
77
|
+
client.user_modify(client.config.username, password: password)
|
78
|
+
rescue StandardError => ex
|
79
|
+
abort "Failed to modify user: #{ex}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'securerandom'
|
16
|
+
require 'oj'
|
17
|
+
require 'yaml'
|
18
|
+
require 'logger'
|
19
|
+
|
20
|
+
require 'cyclid/config'
|
21
|
+
require 'cyclid/auth_methods'
|
22
|
+
|
23
|
+
require 'cyclid/client/api'
|
24
|
+
require 'cyclid/client/user'
|
25
|
+
require 'cyclid/client/organization'
|
26
|
+
require 'cyclid/client/job'
|
27
|
+
require 'cyclid/client/stage'
|
28
|
+
require 'cyclid/client/auth'
|
29
|
+
require 'cyclid/client/health'
|
30
|
+
|
31
|
+
module Cyclid
|
32
|
+
# Cyclid client methods
|
33
|
+
module Client
|
34
|
+
# Tilapia is the standard Cyclid Ruby client. It provides an inteligent
|
35
|
+
# programmable API on top of the standard Cyclid REST API, complete with
|
36
|
+
# automatic signing of HTTP requests and HTTP error handling.
|
37
|
+
#
|
38
|
+
# The client provides interfaces for managing Users, Organizations, Stages
|
39
|
+
# & Jobs. Refer to the documentation for those modules for more information.
|
40
|
+
#
|
41
|
+
# In case you're wondering, this class required a name: it couldn't be
|
42
|
+
# 'Cyclid' and it couldn't be 'Client'. Tilapia are a common type of
|
43
|
+
# Cichlid...
|
44
|
+
class Tilapia
|
45
|
+
attr_reader :config, :logger
|
46
|
+
# @!attribute [r] config
|
47
|
+
# @return [Config] Client configuration object
|
48
|
+
# @!attribute [r] logger
|
49
|
+
# @return [Logger] Client logger object
|
50
|
+
|
51
|
+
# @param [Hash] options
|
52
|
+
# @option options [String] :config_path Fully qualified path to the configuration file
|
53
|
+
# @option options [FixNum] :log_level Logger output level
|
54
|
+
def initialize(options)
|
55
|
+
@config = Config.new(options)
|
56
|
+
|
57
|
+
# Create a logger
|
58
|
+
log_level = options[:log_level] || Logger::FATAL
|
59
|
+
@logger = Logger.new(STDERR)
|
60
|
+
@logger.level = log_level
|
61
|
+
|
62
|
+
# Select the API methods to use
|
63
|
+
@api = case @config.auth
|
64
|
+
when AuthMethods::AUTH_NONE
|
65
|
+
Api::None.new(@config, @logger)
|
66
|
+
when AuthMethods::AUTH_HMAC
|
67
|
+
Api::Hmac.new(@config, @logger)
|
68
|
+
when AuthMethods::AUTH_BASIC
|
69
|
+
Api::Basic.new(@config, @logger)
|
70
|
+
when AuthMethods::AUTH_TOKEN
|
71
|
+
Api::Token.new(@config, @logger)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
include User
|
76
|
+
include Organization
|
77
|
+
include Job
|
78
|
+
include Stage
|
79
|
+
include Auth
|
80
|
+
include Health
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Build a URI for the configured server & required resource
|
85
|
+
def server_uri(path)
|
86
|
+
URI::HTTP.build(host: @config.server,
|
87
|
+
port: @config.port,
|
88
|
+
path: path)
|
89
|
+
end
|
90
|
+
|
91
|
+
def method_missing(method, *args, &block)
|
92
|
+
@api.send(method, *args, &block)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
include AuthMethods
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'uri'
|
16
|
+
require 'net/http'
|
17
|
+
|
18
|
+
module Cyclid
|
19
|
+
# Cyclid client methods
|
20
|
+
module Client
|
21
|
+
# Client API request methods
|
22
|
+
module Api
|
23
|
+
# Base class for API request implementations
|
24
|
+
class Base
|
25
|
+
def initialize(config, logger)
|
26
|
+
@config = config
|
27
|
+
@logger = logger
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add authentication details & perform a GET request
|
31
|
+
def api_get(uri)
|
32
|
+
req = authenticate_request(Net::HTTP::Get.new(uri), uri)
|
33
|
+
|
34
|
+
api_request(uri, req)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add authentication details perform a POST request with a pre-formatted body
|
38
|
+
def api_raw_post(uri, data, content_type)
|
39
|
+
unsigned = Net::HTTP::Post.new(uri)
|
40
|
+
unsigned.content_type = content_type
|
41
|
+
unsigned.body = data
|
42
|
+
|
43
|
+
req = authenticate_request(unsigned, uri)
|
44
|
+
|
45
|
+
api_request(uri, req)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add authentication details perform a POST request with a JSON body
|
49
|
+
def api_json_post(uri, data)
|
50
|
+
json = Oj.dump(data)
|
51
|
+
api_raw_post(uri, json, 'application/json')
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add authentication details perform a POST request with a YAML body
|
55
|
+
def api_yaml_post(uri, data)
|
56
|
+
yaml = YAML.dump(data)
|
57
|
+
api_raw_post(uri, yaml, 'application/x-yaml')
|
58
|
+
end
|
59
|
+
|
60
|
+
# Add authentication details perform a PUT request with a JSON body
|
61
|
+
def api_json_put(uri, data)
|
62
|
+
unsigned = Net::HTTP::Put.new(uri)
|
63
|
+
unsigned.content_type = 'application/json'
|
64
|
+
unsigned.body = Oj.dump(data)
|
65
|
+
|
66
|
+
req = authenticate_request(unsigned, uri)
|
67
|
+
|
68
|
+
api_request(uri, req)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Add authentication details perform a DELETE request
|
72
|
+
def api_delete(uri)
|
73
|
+
req = authenticate_request(Net::HTTP::Delete.new(uri), uri)
|
74
|
+
|
75
|
+
api_request(uri, req)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Perform an API HTTP request & return the parsed response body
|
79
|
+
def api_request(uri, req)
|
80
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
81
|
+
res = http.request(req)
|
82
|
+
|
83
|
+
parse_response(res)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Parse, validate & handle response data
|
87
|
+
def parse_response(res)
|
88
|
+
response_data = Oj.load(res.body)
|
89
|
+
|
90
|
+
# Return immediately if the response was an HTTP 200 OK
|
91
|
+
return response_data if res.code == '200'
|
92
|
+
|
93
|
+
@logger.info "server request failed with error ##{res.code}: \
|
94
|
+
#{response_data['description']}"
|
95
|
+
raise response_data['description']
|
96
|
+
rescue Oj::ParseError => ex
|
97
|
+
@logger.debug "body: #{res.body}\n#{ex}"
|
98
|
+
raise 'failed to decode server response body'
|
99
|
+
end
|
100
|
+
|
101
|
+
# The API does not support non-authenticated requests, so this method
|
102
|
+
# must be implemented.
|
103
|
+
def authenticate_request(_request, _uri)
|
104
|
+
raise NotImplementedError
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
require 'cyclid/client/api/none'
|
112
|
+
require 'cyclid/client/api/hmac'
|
113
|
+
require 'cyclid/client/api/basic'
|
114
|
+
require 'cyclid/client/api/token'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Cyclid
|
16
|
+
# Cyclid client methods
|
17
|
+
module Client
|
18
|
+
# Client API HTTP methods
|
19
|
+
module Api
|
20
|
+
# HTTP Basic auth HTTP methods
|
21
|
+
class Basic < Base
|
22
|
+
# Add the username & password to the request
|
23
|
+
def authenticate_request(request, _uri)
|
24
|
+
request.basic_auth(@config.username, @config.password)
|
25
|
+
return request
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'cyclid/hmac'
|
16
|
+
|
17
|
+
module Cyclid
|
18
|
+
# Cyclid client methods
|
19
|
+
module Client
|
20
|
+
# Client API HTTP methods
|
21
|
+
module Api
|
22
|
+
# HMAC signed HTTP methods
|
23
|
+
class Hmac < Base
|
24
|
+
# Sign the request with HMAC
|
25
|
+
def authenticate_request(request, uri)
|
26
|
+
algorithm = 'sha256'
|
27
|
+
signer = Cyclid::HMAC::Signer.new(algorithm)
|
28
|
+
|
29
|
+
method = if request.is_a? Net::HTTP::Get
|
30
|
+
'GET'
|
31
|
+
elsif request.is_a? Net::HTTP::Post
|
32
|
+
'POST'
|
33
|
+
elsif request.is_a? Net::HTTP::Put
|
34
|
+
'PUT'
|
35
|
+
elsif request.is_a? Net::HTTP::Delete
|
36
|
+
'DELETE'
|
37
|
+
else
|
38
|
+
raise "invalid request method #{request.inspect}"
|
39
|
+
end
|
40
|
+
|
41
|
+
nonce = SecureRandom.hex
|
42
|
+
headers = signer.sign_request(uri.path,
|
43
|
+
@config.secret,
|
44
|
+
auth_header_format: '%{auth_scheme} %{username}:%{signature}', # rubocop:disable Metrics/LineLength
|
45
|
+
username: @config.username,
|
46
|
+
nonce: nonce,
|
47
|
+
method: method)
|
48
|
+
|
49
|
+
headers[0].each do |k, v|
|
50
|
+
request[k] = v
|
51
|
+
end
|
52
|
+
request['X-HMAC-Algorithm'] = algorithm
|
53
|
+
|
54
|
+
return request
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Cyclid
|
16
|
+
# Cyclid client methods
|
17
|
+
module Client
|
18
|
+
# Client API HTTP methods
|
19
|
+
module Api
|
20
|
+
# Only works for non-authenticated request (E.g. healthchecks)
|
21
|
+
class None < Base
|
22
|
+
# Do nothing
|
23
|
+
def authenticate_request(request, _uri)
|
24
|
+
request
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
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 implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Cyclid
|
16
|
+
# Cyclid client methods
|
17
|
+
module Client
|
18
|
+
# Client API HTTP methods
|
19
|
+
module Api
|
20
|
+
# JWT token based HTTP methods
|
21
|
+
class Token < Base
|
22
|
+
# Add the token to the request
|
23
|
+
def authenticate_request(request, _uri)
|
24
|
+
request.add_field('Authorization', "Token #{@config.username}:#{@config.token}")
|
25
|
+
return request
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|