terraform-enterprise-cli 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +373 -0
- data/README.md +75 -0
- data/bin/tfe +4 -0
- data/lib/terraform-enterprise-command-line.rb +79 -0
- data/lib/terraform_enterprise/command_line/command.rb +61 -0
- data/lib/terraform_enterprise/command_line/commands/configuration_versions.rb +37 -0
- data/lib/terraform_enterprise/command_line/commands/oauth_tokens.rb +15 -0
- data/lib/terraform_enterprise/command_line/commands/organizations.rb +30 -0
- data/lib/terraform_enterprise/command_line/commands/policies.rb +72 -0
- data/lib/terraform_enterprise/command_line/commands/policy_checks.rb +29 -0
- data/lib/terraform_enterprise/command_line/commands/runs.rb +88 -0
- data/lib/terraform_enterprise/command_line/commands/teams.rb +33 -0
- data/lib/terraform_enterprise/command_line/commands/variables.rb +61 -0
- data/lib/terraform_enterprise/command_line/commands/workspaces.rb +87 -0
- data/lib/terraform_enterprise/command_line/formatter.rb +131 -0
- data/lib/terraform_enterprise/command_line/strings.rb +149 -0
- data/lib/terraform_enterprise/command_line/util.rb +50 -0
- data/lib/terraform_enterprise/command_line/version.rb +5 -0
- metadata +144 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'terraform_enterprise/command_line/command'
|
2
|
+
|
3
|
+
module TerraformEnterprise
|
4
|
+
module CommandLine
|
5
|
+
class VariablesCommand < TerraformEnterprise::CommandLine::Command
|
6
|
+
ATTR_STR = STRINGS[:variables][:attributes]
|
7
|
+
CMD_STR = STRINGS[:variables][:commands]
|
8
|
+
|
9
|
+
desc 'list', CMD_STR[:list]
|
10
|
+
option :table, type: :boolean, default: true, desc: STRINGS[:options][:table]
|
11
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
12
|
+
option :workspace, type: :string, desc: ATTR_STR[:workspace]
|
13
|
+
def list
|
14
|
+
render client.variables.list(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'create <key> <value>', CMD_STR[:create]
|
18
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
19
|
+
option :workspace, required: true, type: :string, desc: ATTR_STR[:workspace]
|
20
|
+
option :category, default: 'terraform', type: :string, desc: ATTR_STR[:category], enum:['terraform', 'env']
|
21
|
+
option :hcl, default: false, type: :boolean, desc: ATTR_STR[:hcl]
|
22
|
+
option :sensitive, default: false, type: :boolean, desc: ATTR_STR[:sensitive]
|
23
|
+
def create(key, value)
|
24
|
+
params = {
|
25
|
+
category: options[:category],
|
26
|
+
hcl: options[:hcl],
|
27
|
+
key: key,
|
28
|
+
organization: options[:organization],
|
29
|
+
sensitive: options[:sensitive],
|
30
|
+
value: value,
|
31
|
+
workspace: options[:workspace],
|
32
|
+
}
|
33
|
+
render client.variables.create(params)
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'update <id>', CMD_STR[:update]
|
37
|
+
option :hcl, type: :boolean, desc: ATTR_STR[:hcl]
|
38
|
+
option :sensitive, type: :boolean, desc: ATTR_STR[:sensitive]
|
39
|
+
option :key, type: :string, desc: ATTR_STR[:key]
|
40
|
+
option :value, type: :string, desc: ATTR_STR[:value]
|
41
|
+
def update(id)
|
42
|
+
params = {id: id}
|
43
|
+
params[:hcl] = options[:hcl] if options.include?('hcl')
|
44
|
+
params[:key] = options[:key] if options[:key]
|
45
|
+
params[:sensitive] = options[:sensitive] if options.include?('sensitive')
|
46
|
+
params[:value] = options[:value] if options[:value]
|
47
|
+
render client.variables.update(params)
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'get <id>', CMD_STR[:get]
|
51
|
+
def get(id)
|
52
|
+
render client.variables.get(id:id)
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'delete <id>', CMD_STR[:delete]
|
56
|
+
def delete(id)
|
57
|
+
render client.variables.delete(id: id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'terraform_enterprise/command_line/command'
|
2
|
+
|
3
|
+
module TerraformEnterprise
|
4
|
+
module CommandLine
|
5
|
+
class WorkspacesCommand < TerraformEnterprise::CommandLine::Command
|
6
|
+
ATTR_STR = STRINGS[:workspaces][:attributes]
|
7
|
+
CMD_STR = STRINGS[:workspaces][:commands]
|
8
|
+
|
9
|
+
desc 'list', CMD_STR[:list]
|
10
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
11
|
+
option :table, type: :boolean, default: true, desc: STRINGS[:options][:table]
|
12
|
+
def list
|
13
|
+
render client.workspaces.list(options), except:[:permissions, :actions, :environment, 'created-at']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'create <name>', CMD_STR[:create]
|
17
|
+
option :terraform_version, type: :string, desc: ATTR_STR[:terraform_version]
|
18
|
+
option :working_directory, type: :string, desc: ATTR_STR[:working_directory]
|
19
|
+
option :oauth_token, type: :string, desc: ATTR_STR[:oauth_token]
|
20
|
+
option :branch, type: :string, desc: ATTR_STR[:branch]
|
21
|
+
option :ingress_submodules, type: :boolean, desc: ATTR_STR[:ingress_submodules]
|
22
|
+
option :repo, type: :string, desc: ATTR_STR[:repos]
|
23
|
+
option :import_legacy_environment, type: :string, desc: ATTR_STR[:import_legacy]
|
24
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
25
|
+
def create(name)
|
26
|
+
params = {
|
27
|
+
organization: options[:organization],
|
28
|
+
name: name,
|
29
|
+
'working-directory' => options[:working_directory] || '',
|
30
|
+
}
|
31
|
+
if options[:repo] && options[:oauth_token]
|
32
|
+
repo = {}
|
33
|
+
repo['branch'] = options[:branch] || ''
|
34
|
+
repo['identifier'] = options[:repo]
|
35
|
+
repo['oauth-token-id'] = options[:oauth_token]
|
36
|
+
repo['ingress-submodules'] = options[:ingress_submodules] || false
|
37
|
+
params['vcs-repo'] = repo
|
38
|
+
end
|
39
|
+
|
40
|
+
params['migration-environment'] = options[:import_legacy_environment] if options[:import_legacy_environment]
|
41
|
+
params['terraform_version'] = options[:terraform_version] if options[:terraform_version]
|
42
|
+
render client.workspaces.create(params), except:[:permissions, :actions, :environment]
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'get <name>', CMD_STR[:get]
|
46
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
47
|
+
def get(name)
|
48
|
+
params = {
|
49
|
+
organization: options[:organization],
|
50
|
+
workspace: name
|
51
|
+
}
|
52
|
+
render client.workspaces.get(params), except:[:permissions, :environment]
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'delete <name>', CMD_STR[:delete]
|
56
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
57
|
+
def delete(name)
|
58
|
+
params = {
|
59
|
+
organization: options[:organization],
|
60
|
+
workspace: name
|
61
|
+
}
|
62
|
+
render client.workspaces.delete(params), except:[:permissions, :actions, :environment]
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'update <name>', CMD_STR[:update]
|
66
|
+
option :working_directory, type: :string, desc: ATTR_STR[:working_directory]
|
67
|
+
option :terraform_version, type: :string, desc: ATTR_STR[:terraform_version]
|
68
|
+
option :auto_apply, type: :boolean, desc: ATTR_STR[:auto_apply]
|
69
|
+
option :organization, required: true, type: :string, desc: ATTR_STR[:organization]
|
70
|
+
def update(name)
|
71
|
+
params = options
|
72
|
+
params[:workspace] = name
|
73
|
+
render client.workspaces.update(params), except:[:permissions, :environment]
|
74
|
+
end
|
75
|
+
|
76
|
+
desc 'lock <id>', CMD_STR[:lock]
|
77
|
+
def lock(id)
|
78
|
+
render client.workspaces.action(action: :lock, id: id), except:[:permissions, :environment]
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'unlock <id>', CMD_STR[:unlock]
|
82
|
+
def unlock(id)
|
83
|
+
render client.workspaces.action(action: :unlock, id: id), except:[:permissions, :environment]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'colorize'
|
3
|
+
require 'terminal-table'
|
4
|
+
|
5
|
+
require 'terraform-enterprise-client'
|
6
|
+
|
7
|
+
module TerraformEnterprise
|
8
|
+
module CommandLine
|
9
|
+
# Module with render method to render the Resource object
|
10
|
+
class Formatter
|
11
|
+
def render(obj, options = {})
|
12
|
+
String.disable_colorization = !options[:color]
|
13
|
+
|
14
|
+
if !obj.is_a?(TerraformEnterprise::API::Response)
|
15
|
+
unkown_response(obj)
|
16
|
+
elsif obj.success?
|
17
|
+
render_resource(obj, options)
|
18
|
+
elsif obj.errors?
|
19
|
+
render_errors(obj)
|
20
|
+
else
|
21
|
+
unkown_response(obj.body)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(message)
|
26
|
+
puts "Error: #{message}".red
|
27
|
+
end
|
28
|
+
|
29
|
+
def success(message)
|
30
|
+
puts message.green
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def unkown_response(obj)
|
36
|
+
puts 'Unknown response'.yellow
|
37
|
+
puts obj
|
38
|
+
exit(false)
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_resource(obj, options)
|
42
|
+
if obj.resources
|
43
|
+
puts render_resource_list(obj.resources, options)
|
44
|
+
elsif obj.resource
|
45
|
+
puts render_resource_item(obj.resource, options)
|
46
|
+
else
|
47
|
+
success "Success (#{obj.code})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def render_errors(obj)
|
52
|
+
obj.errors.each do |error|
|
53
|
+
message = error['detail'] || error['title'] || error.to_s
|
54
|
+
code = error['status'] || obj.code
|
55
|
+
error "[#{code}] #{message}"
|
56
|
+
end
|
57
|
+
exit(false)
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_resource(resource, options)
|
61
|
+
parsed_resource = flatten_dotted_hash(resource.attributes)
|
62
|
+
if resource.id
|
63
|
+
parsed_resource = { 'id' => resource.id }.merge(parsed_resource)
|
64
|
+
end
|
65
|
+
(options[:except] || []).each do |excluded|
|
66
|
+
parsed_resource.delete_if do |key, _|
|
67
|
+
key.to_s.start_with?(excluded.to_s)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
if options[:only] && !options[:only].empty?
|
71
|
+
parsed_resource.select! do |key, _|
|
72
|
+
options[:only].any? do |included|
|
73
|
+
key.to_s.start_with?(included.to_s)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
parsed_resource
|
78
|
+
end
|
79
|
+
|
80
|
+
def render_resource_item(resource, options)
|
81
|
+
parsed_resource = parse_resource(resource, options)
|
82
|
+
parsed_resource.keys.map do |key|
|
83
|
+
value = parsed_resource[key]
|
84
|
+
options[:value] ? value : "#{key.bold}: #{value}"
|
85
|
+
end.join("\n")
|
86
|
+
end
|
87
|
+
|
88
|
+
def render_resource_list(resources, options)
|
89
|
+
if options[:table] && !options[:value]
|
90
|
+
parsed_resources = resources.map do |resource|
|
91
|
+
parse_resource(resource, options)
|
92
|
+
end
|
93
|
+
keys = parsed_resources.map(&:keys).flatten.uniq
|
94
|
+
rows = parsed_resources.map do |resource|
|
95
|
+
keys.map { |key| resource[key] }
|
96
|
+
end
|
97
|
+
table = Terminal::Table.new headings: keys, rows: rows
|
98
|
+
table
|
99
|
+
else
|
100
|
+
line_separator = "\n#{'-' * 10}\n"
|
101
|
+
out = resources.map do |resource|
|
102
|
+
render_resource_item(resource, options)
|
103
|
+
end
|
104
|
+
out.join(line_separator)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def flatten_hash(hash, new_key = [], new_hash = {})
|
109
|
+
if hash.is_a?(Array)
|
110
|
+
hash.each_with_index do |item, obj|
|
111
|
+
flatten_hash(item, new_key + [obj], new_hash)
|
112
|
+
end
|
113
|
+
elsif hash.is_a?(Hash)
|
114
|
+
hash.each do |key, value|
|
115
|
+
flatten_hash(value, new_key + [key], new_hash)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
return new_hash.update(new_key => hash)
|
119
|
+
end
|
120
|
+
new_hash
|
121
|
+
end
|
122
|
+
|
123
|
+
def flatten_dotted_hash(source)
|
124
|
+
flat = flatten_hash(source)
|
125
|
+
flat.keys.each_with_object({}) do |key, h|
|
126
|
+
h[key.join('.')] = flat[key]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module TerraformEnterprise
|
4
|
+
module CommandLine
|
5
|
+
STRINGS = {
|
6
|
+
options: {
|
7
|
+
table: 'Format list output as a table',
|
8
|
+
host: 'Set host address for private Terraform Enterprise',
|
9
|
+
token: 'Set the auth token, defaults to TFE_TOKEN environment variable',
|
10
|
+
color: 'If disabled the ANSI color codes will not be used',
|
11
|
+
except: 'List of fields that should not be displayed',
|
12
|
+
only: 'List of fields that should be displayed',
|
13
|
+
all: 'Return all fields, not just summary',
|
14
|
+
value: 'Only return the value; i.e. do not show keys',
|
15
|
+
debug: 'Show debug logs'
|
16
|
+
},
|
17
|
+
push: {
|
18
|
+
attributes: {
|
19
|
+
path: 'Path of directory or tar.gz file to push to the workspace'
|
20
|
+
},
|
21
|
+
commands: {
|
22
|
+
push: 'Pushes the configuration to the workspace'
|
23
|
+
}
|
24
|
+
},
|
25
|
+
workspaces: {
|
26
|
+
attributes: {
|
27
|
+
terraform_version: 'Version of Terraform to use for this workspace.',
|
28
|
+
working_directory: 'Relative path that Terraform will execute within.',
|
29
|
+
oauth_token: 'VCS Connection (OAuth Conection + Token) to use as identified; obtained from the oauth_tokens subcommand.',
|
30
|
+
branch: 'Repository branch that Terraform will execute from.',
|
31
|
+
ingress_submodules: 'Submodules should be fetched when cloning the VCS repository.',
|
32
|
+
repo: 'Reference to VCS repository in the format :org/:repo.',
|
33
|
+
import_legacy: 'Specifies the legacy Environment to use as the source of the migration/',
|
34
|
+
organization: 'Organization to which this workspaces belongs to.',
|
35
|
+
auto_apply: 'Auto-apply enabled'
|
36
|
+
},
|
37
|
+
commands: {
|
38
|
+
create: 'Create a new workspace',
|
39
|
+
list: 'List workspaces in the organization',
|
40
|
+
get: 'Get workspace details by name',
|
41
|
+
delete: 'Delete the workspace',
|
42
|
+
update: 'Update the workspace',
|
43
|
+
lock: 'Lock the workspace by workspace ID',
|
44
|
+
unlock: 'Unlock the workspace by workspace ID'
|
45
|
+
}
|
46
|
+
},
|
47
|
+
configuration_versions: {
|
48
|
+
attributes: {
|
49
|
+
workspace_id: 'Workspace ID of the workspace to which the configuration version belongs to.',
|
50
|
+
path: 'Path to the tar.gz file'
|
51
|
+
},
|
52
|
+
commands: {
|
53
|
+
create: 'Create a new configuration version',
|
54
|
+
list: 'List configuration versions in the organization',
|
55
|
+
get: 'Get configuration version details by name',
|
56
|
+
upload: 'Upload a file to the configuration version'
|
57
|
+
}
|
58
|
+
},
|
59
|
+
organizations: {
|
60
|
+
commands: {
|
61
|
+
create: 'Create a new organization',
|
62
|
+
list: 'List all the organizations',
|
63
|
+
get: 'Get organization details by name',
|
64
|
+
delete: 'Delete the organization'
|
65
|
+
},
|
66
|
+
attributes: {}
|
67
|
+
},
|
68
|
+
teams: {
|
69
|
+
commands: {
|
70
|
+
create: 'Create a new team',
|
71
|
+
delete: 'Delete the team by ID',
|
72
|
+
list: 'List teams in organization',
|
73
|
+
get: 'Get team details'
|
74
|
+
},
|
75
|
+
attributes: {
|
76
|
+
organization: 'Organization to which this Team belongs to.'
|
77
|
+
}
|
78
|
+
},
|
79
|
+
policies: {
|
80
|
+
commands: {
|
81
|
+
create: 'Create a new policy',
|
82
|
+
delete: 'Delete a policy by ID',
|
83
|
+
list: 'List policies in organization',
|
84
|
+
get: 'Get policy details',
|
85
|
+
upload: 'Upload a policy'
|
86
|
+
},
|
87
|
+
attributes: {
|
88
|
+
organization: 'Organization to which this Team belongs to.',
|
89
|
+
mode: 'Policy mode, hard-mandatory, soft-mandatory or advisory',
|
90
|
+
name: 'Policy name'
|
91
|
+
}
|
92
|
+
},
|
93
|
+
runs: {
|
94
|
+
commands: {
|
95
|
+
create: 'Create a new run',
|
96
|
+
list: 'List runs in a workspace',
|
97
|
+
get: 'Get run details',
|
98
|
+
apply: 'Apply the plan',
|
99
|
+
discard: 'Discard the plan',
|
100
|
+
log: 'Return logs for the plan or apply'
|
101
|
+
},
|
102
|
+
attributes: {
|
103
|
+
workspace_id: 'Workspace ID of which the run belongs to.',
|
104
|
+
configuration_version_id: 'Configuration Version ID of the configuration version to run',
|
105
|
+
destroy: 'The run should be a destroy plan',
|
106
|
+
comment: 'Add a comment for the action',
|
107
|
+
follow: 'Follow the logs until output is complete',
|
108
|
+
event: 'Run event for which to get logs (plan or apply)'
|
109
|
+
}
|
110
|
+
},
|
111
|
+
policy_checks: {
|
112
|
+
commands: {
|
113
|
+
list: 'List policy checks on run',
|
114
|
+
override: 'Override the soft-mandatory or advisory policy'
|
115
|
+
},
|
116
|
+
attributes: {
|
117
|
+
run_id: 'Run ID to which the policy check belongs to.',
|
118
|
+
comment: 'Add a comment for the action'
|
119
|
+
}
|
120
|
+
},
|
121
|
+
oauth_tokens: {
|
122
|
+
commands: {
|
123
|
+
list: 'List the OAuth tokens in the organization'
|
124
|
+
},
|
125
|
+
attributes: {
|
126
|
+
organization: 'Organization to which this OAuth Token belongs to.'
|
127
|
+
}
|
128
|
+
},
|
129
|
+
variables: {
|
130
|
+
commands: {
|
131
|
+
create: 'Create a new variable',
|
132
|
+
delete: 'Delete the variable by ID',
|
133
|
+
get: 'Get variable details',
|
134
|
+
list: 'List variables in organization',
|
135
|
+
update: 'Update a variable by ID'
|
136
|
+
},
|
137
|
+
attributes: {
|
138
|
+
organization: 'Organization to which this Variable belongs to.',
|
139
|
+
workspace: 'Workspace to which this Variable belongs to.',
|
140
|
+
category: 'The type of category, probably "terraform" or "env"',
|
141
|
+
hcl: 'Variable should be parsed using HCL',
|
142
|
+
sensitive: 'Variable should be marked as sensitive',
|
143
|
+
value: 'Variable value',
|
144
|
+
key: 'Variable key'
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}.freeze
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'zlib'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module TerraformEnterprise
|
6
|
+
module CommandLine
|
7
|
+
module Util
|
8
|
+
# Module to perform a tar and gz of a directory
|
9
|
+
module Tar
|
10
|
+
def tar(path)
|
11
|
+
tarfile = StringIO.new('')
|
12
|
+
Gem::Package::TarWriter.new(tarfile) do |tar|
|
13
|
+
Dir[File.join(path, '**/*')].each do |file|
|
14
|
+
mode = File.stat(file).mode
|
15
|
+
relative_file = file.sub /^#{Regexp.escape(path)}\/?/, ''
|
16
|
+
|
17
|
+
if File.directory?(file)
|
18
|
+
tar.mkdir relative_file, mode
|
19
|
+
else
|
20
|
+
tar.add_file relative_file, mode do |tf|
|
21
|
+
File.open(file, 'rb') { |f| tf.write f.read }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
tarfile.rewind
|
28
|
+
tarfile
|
29
|
+
end
|
30
|
+
|
31
|
+
def gzip(tarfile)
|
32
|
+
gzip_string = StringIO.new('')
|
33
|
+
gzip_writer = Zlib::GzipWriter.new(gzip_string)
|
34
|
+
gzip_writer.write tarfile.string
|
35
|
+
gzip_writer.close
|
36
|
+
gzip_string.string
|
37
|
+
end
|
38
|
+
|
39
|
+
def tarball(path)
|
40
|
+
full_path = File.expand_path(path)
|
41
|
+
if File.directory?(full_path)
|
42
|
+
gzip(tar(full_path))
|
43
|
+
else
|
44
|
+
File.read(full_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|