lex-tfe 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 70b7f70e681039dbaccf9544ca6a99eba1506c8918fb3b01245e4d4c6e7e971e
4
+ data.tar.gz: 44fac6f7f48a727df2d5d416b62c7f2572216434817560d0db75d9d323ba4084
5
+ SHA512:
6
+ metadata.gz: 618c76447a6b4091b56c77c85ee47c4c651239bdc242fef20d6d0eb01b60170e876ceb07c5d4d3149d5effdaebf72c9db140e883fc733936699322876dbac559
7
+ data.tar.gz: 8486d8c5fae9938417fbde075d0153fbb0633087f9cf1f45586039a8ed805e006de90167ba761958f78f75d31052899a63c9979efcfb9b91fbe9ada6b60eb07e
@@ -0,0 +1,16 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+
7
+ jobs:
8
+ ci:
9
+ uses: LegionIO/.github/.github/workflows/ci.yml@main
10
+
11
+ release:
12
+ needs: ci
13
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
14
+ uses: LegionIO/.github/.github/workflows/release.yml@main
15
+ secrets:
16
+ rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,57 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.4
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 160
8
+
9
+ Layout/SpaceAroundEqualsInParameterDefault:
10
+ EnforcedStyle: space
11
+
12
+ Layout/HashAlignment:
13
+ EnforcedHashRocketStyle: table
14
+ EnforcedColonStyle: table
15
+
16
+ Metrics/MethodLength:
17
+ Max: 50
18
+
19
+ Metrics/ClassLength:
20
+ Max: 1500
21
+
22
+ Metrics/ModuleLength:
23
+ Max: 1500
24
+
25
+ Metrics/BlockLength:
26
+ Max: 40
27
+ Exclude:
28
+ - 'spec/**/*'
29
+
30
+ Metrics/ParameterLists:
31
+ Max: 8
32
+
33
+ Metrics/AbcSize:
34
+ Max: 60
35
+
36
+ Metrics/CyclomaticComplexity:
37
+ Max: 15
38
+
39
+ Metrics/PerceivedComplexity:
40
+ Max: 17
41
+
42
+ Style/Documentation:
43
+ Enabled: false
44
+
45
+ Style/SymbolArray:
46
+ Enabled: true
47
+
48
+ Style/FrozenStringLiteralComment:
49
+ Enabled: true
50
+ EnforcedStyle: always
51
+
52
+ Naming/FileName:
53
+ Enabled: false
54
+
55
+ Naming/MethodParameterName:
56
+ AllowedNames:
57
+ - id
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0]
4
+
5
+ ### Added
6
+ - Initial release
7
+ - Runners: Workspaces, Runs, Plans, Applies, Variables, VariableSets, Projects, Organizations, StateVersions, PolicySets
8
+ - Standalone Client class with configurable url, token, and read_only
9
+ - Read-only mode support via ReadOnlyError on mutating operations
10
+ - Bearer token authentication
11
+ - JSON:API content type support
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop'
11
+ gem 'simplecov'
12
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Iverson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # lex-tfe
2
+
3
+ LegionIO Extension for Terraform Enterprise (TFE) and HCP Terraform. Provides runners for managing workspaces, runs, plans, applies, variables, variable sets, projects, organizations, state versions, and policy sets via the TFE REST API v2.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'lex-tfe'
11
+ ```
12
+
13
+ Or install directly:
14
+
15
+ ```bash
16
+ gem install lex-tfe
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ Settings are read from `Legion::Settings[:extensions][:tfe]` when running inside the LegionIO framework:
22
+
23
+ | Setting | Description | Default |
24
+ |------------|-------------------------------------------------------|--------------------------------|
25
+ | `url` | TFE/HCP Terraform base URL | `https://app.terraform.io` |
26
+ | `token` | API token (Bearer auth) | `nil` |
27
+ | `read_only`| Prevent create/update/delete operations | `false` |
28
+
29
+ Supported base URLs:
30
+ - `https://app.terraform.io` — HCP Terraform (SaaS)
31
+ - `https://terraform.uhg.com` — UHG app team TFE cluster
32
+ - `https://tfe-arc.uhg.com` — UHG Grid platform TFE cluster
33
+ - Any custom TFE instance
34
+
35
+ ## Standalone Client Usage
36
+
37
+ ```ruby
38
+ require 'lex-tfe'
39
+
40
+ client = Legion::Extensions::Tfe::Client.new(
41
+ url: 'https://terraform.uhg.com',
42
+ token: 'your-api-token',
43
+ read_only: false
44
+ )
45
+
46
+ # List workspaces in an organization
47
+ client.list(organization: 'my-org')
48
+
49
+ # Get a specific workspace
50
+ client.get(workspace_id: 'ws-abc123')
51
+
52
+ # Create a workspace
53
+ client.create(organization: 'my-org', name: 'new-workspace', auto_apply: true)
54
+
55
+ # Trigger a run
56
+ client.create_run(workspace_id: 'ws-abc123', message: 'triggered by CI', auto_apply: true)
57
+
58
+ # List variables
59
+ client.list_variables(workspace_id: 'ws-abc123')
60
+
61
+ # Create a variable
62
+ client.create_variable(
63
+ workspace_id: 'ws-abc123',
64
+ key: 'AWS_REGION',
65
+ value: 'us-east-2',
66
+ category: 'env'
67
+ )
68
+ ```
69
+
70
+ ### Read-only mode
71
+
72
+ ```ruby
73
+ client = Legion::Extensions::Tfe::Client.new(
74
+ url: 'https://terraform.uhg.com',
75
+ token: 'read-only-token',
76
+ read_only: true
77
+ )
78
+
79
+ # Safe - read operations work normally
80
+ client.list(organization: 'my-org')
81
+
82
+ # Raises Legion::Extensions::Tfe::ReadOnlyError
83
+ client.create(organization: 'my-org', name: 'ws', read_only: client.opts[:read_only])
84
+ ```
85
+
86
+ ## Runners
87
+
88
+ ### Workspaces
89
+
90
+ | Method | Description |
91
+ |-------------|----------------------------------------|
92
+ | `list` | List workspaces in an organization |
93
+ | `get` | Get a workspace by ID |
94
+ | `create` | Create a workspace |
95
+ | `update` | Update a workspace |
96
+ | `delete` | Delete a workspace |
97
+ | `lock` | Lock a workspace |
98
+ | `unlock` | Unlock a workspace |
99
+
100
+ ### Runs
101
+
102
+ | Method | Description |
103
+ |---------------|---------------------------------|
104
+ | `list_runs` | List runs for a workspace |
105
+ | `get_run` | Get a run by ID |
106
+ | `create_run` | Queue a new run |
107
+ | `apply_run` | Apply a run |
108
+ | `discard_run` | Discard a run |
109
+ | `cancel_run` | Cancel a run |
110
+
111
+ ### Plans
112
+
113
+ | Method | Description |
114
+ |------------------------|------------------------------|
115
+ | `get_plan` | Get a plan by ID |
116
+ | `get_plan_json_output` | Get the plan JSON output |
117
+ | `get_plan_log` | Get the plan log |
118
+
119
+ ### Applies
120
+
121
+ | Method | Description |
122
+ |-----------------|-------------------------|
123
+ | `get_apply` | Get an apply by ID |
124
+ | `get_apply_log` | Get the apply log |
125
+
126
+ ### Variables
127
+
128
+ | Method | Description |
129
+ |--------------------|-----------------------------------|
130
+ | `list_variables` | List workspace variables |
131
+ | `create_variable` | Create a workspace variable |
132
+ | `update_variable` | Update a workspace variable |
133
+ | `delete_variable` | Delete a workspace variable |
134
+
135
+ ### Variable Sets
136
+
137
+ | Method | Description |
138
+ |-------------------------|------------------------------------------|
139
+ | `list_variable_sets` | List variable sets in an organization |
140
+ | `get_variable_set` | Get a variable set by ID |
141
+ | `create_variable_set` | Create a variable set |
142
+ | `update_variable_set` | Update a variable set |
143
+ | `delete_variable_set` | Delete a variable set |
144
+ | `list_varset_variables` | List variables in a variable set |
145
+ | `add_varset_variable` | Add a variable to a variable set |
146
+
147
+ ### Projects
148
+
149
+ | Method | Description |
150
+ |------------------|--------------------------------------|
151
+ | `list_projects` | List projects in an organization |
152
+ | `get_project` | Get a project by ID |
153
+ | `create_project` | Create a project |
154
+ | `update_project` | Update a project |
155
+ | `delete_project` | Delete a project |
156
+
157
+ ### Organizations
158
+
159
+ | Method | Description |
160
+ |----------------------|--------------------------|
161
+ | `list_organizations` | List all organizations |
162
+ | `get_organization` | Get an organization |
163
+
164
+ ### State Versions
165
+
166
+ | Method | Description |
167
+ |---------------------------|-----------------------------------------|
168
+ | `list_state_versions` | List state versions for a workspace |
169
+ | `get_state_version` | Get a state version by ID |
170
+ | `get_current_state_version` | Get the current state version |
171
+
172
+ ### Policy Sets
173
+
174
+ | Method | Description |
175
+ |-----------------------------|---------------------------------------------|
176
+ | `list_policy_sets` | List policy sets in an organization |
177
+ | `get_policy_set` | Get a policy set by ID |
178
+ | `list_workspace_policy_sets`| List policy sets attached to a workspace |
179
+
180
+ ## Development
181
+
182
+ ```bash
183
+ bundle install
184
+ bundle exec rspec
185
+ bundle exec rubocop
186
+ ```
data/lex-tfe.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/tfe/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-tfe'
7
+ spec.version = Legion::Extensions::Tfe::VERSION
8
+ spec.authors = ['Matthew Iverson']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX TFE'
12
+ spec.description = 'Connects LegionIO to Terraform Enterprise / HCP Terraform'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-tfe'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-tfe'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-tfe'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-tfe'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-tfe/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'faraday', '>= 2.0'
30
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/helpers/client'
4
+ require 'legion/extensions/tfe/runners/workspaces'
5
+ require 'legion/extensions/tfe/runners/runs'
6
+ require 'legion/extensions/tfe/runners/plans'
7
+ require 'legion/extensions/tfe/runners/applies'
8
+ require 'legion/extensions/tfe/runners/variables'
9
+ require 'legion/extensions/tfe/runners/variable_sets'
10
+ require 'legion/extensions/tfe/runners/projects'
11
+ require 'legion/extensions/tfe/runners/organizations'
12
+ require 'legion/extensions/tfe/runners/state_versions'
13
+ require 'legion/extensions/tfe/runners/policy_sets'
14
+
15
+ module Legion
16
+ module Extensions
17
+ module Tfe
18
+ class Client
19
+ include Helpers::Client
20
+ include Runners::Workspaces
21
+ include Runners::Runs
22
+ include Runners::Plans
23
+ include Runners::Applies
24
+ include Runners::Variables
25
+ include Runners::VariableSets
26
+ include Runners::Projects
27
+ include Runners::Organizations
28
+ include Runners::StateVersions
29
+ include Runners::PolicySets
30
+
31
+ attr_reader :opts
32
+
33
+ def initialize(url: 'https://app.terraform.io', token: nil, read_only: false, **extra)
34
+ @opts = { url: url, token: token, read_only: read_only, **extra }
35
+ end
36
+
37
+ def connection(**override)
38
+ super(**@opts.merge(override.compact))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Tfe
6
+ class ReadOnlyError < StandardError; end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Tfe
8
+ module Helpers
9
+ module Client
10
+ def connection(url: 'https://app.terraform.io', token: nil, **_opts)
11
+ Faraday.new(url: url) do |conn|
12
+ conn.request :json
13
+ conn.response :json, content_type: /\bjson$/
14
+ conn.headers['Authorization'] = "Bearer #{token}" if token
15
+ conn.headers['Content-Type'] = 'application/vnd.api+json'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/helpers/client'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Tfe
8
+ module Runners
9
+ module Applies
10
+ include Legion::Extensions::Tfe::Helpers::Client
11
+
12
+ def get_apply(apply_id:, url: nil, token: nil, **)
13
+ resp = connection(url: url, token: token).get("/api/v2/applies/#{apply_id}")
14
+ resp.body
15
+ end
16
+
17
+ def get_apply_log(apply_id:, url: nil, token: nil, **)
18
+ resp = connection(url: url, token: token).get("/api/v2/applies/#{apply_id}/log-read")
19
+ resp.body
20
+ end
21
+
22
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
23
+ Legion::Extensions::Helpers.const_defined?(:Lex)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/helpers/client'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Tfe
8
+ module Runners
9
+ module Organizations
10
+ include Legion::Extensions::Tfe::Helpers::Client
11
+
12
+ def list_organizations(url: nil, token: nil, **)
13
+ resp = connection(url: url, token: token).get('/api/v2/organizations')
14
+ resp.body
15
+ end
16
+
17
+ def get_organization(organization:, url: nil, token: nil, **)
18
+ resp = connection(url: url, token: token).get("/api/v2/organizations/#{organization}")
19
+ resp.body
20
+ end
21
+
22
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
23
+ Legion::Extensions::Helpers.const_defined?(:Lex)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/helpers/client'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Tfe
8
+ module Runners
9
+ module Plans
10
+ include Legion::Extensions::Tfe::Helpers::Client
11
+
12
+ def get_plan(plan_id:, url: nil, token: nil, **)
13
+ resp = connection(url: url, token: token).get("/api/v2/plans/#{plan_id}")
14
+ resp.body
15
+ end
16
+
17
+ def get_plan_json_output(plan_id:, url: nil, token: nil, **)
18
+ resp = connection(url: url, token: token).get("/api/v2/plans/#{plan_id}/json-output")
19
+ resp.body
20
+ end
21
+
22
+ def get_plan_log(plan_id:, url: nil, token: nil, **)
23
+ resp = connection(url: url, token: token).get("/api/v2/plans/#{plan_id}/log-read")
24
+ resp.body
25
+ end
26
+
27
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
28
+ Legion::Extensions::Helpers.const_defined?(:Lex)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/helpers/client'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Tfe
8
+ module Runners
9
+ module PolicySets
10
+ include Legion::Extensions::Tfe::Helpers::Client
11
+
12
+ def list_policy_sets(organization:, url: nil, token: nil, **)
13
+ resp = connection(url: url, token: token).get("/api/v2/organizations/#{organization}/policy-sets")
14
+ resp.body
15
+ end
16
+
17
+ def get_policy_set(policy_set_id:, url: nil, token: nil, **)
18
+ resp = connection(url: url, token: token).get("/api/v2/policy-sets/#{policy_set_id}")
19
+ resp.body
20
+ end
21
+
22
+ def list_workspace_policy_sets(workspace_id:, url: nil, token: nil, **)
23
+ resp = connection(url: url, token: token).get("/api/v2/workspaces/#{workspace_id}/relationships/policy-sets")
24
+ resp.body
25
+ end
26
+
27
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
28
+ Legion::Extensions::Helpers.const_defined?(:Lex)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/errors'
4
+ require 'legion/extensions/tfe/helpers/client'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Tfe
9
+ module Runners
10
+ module Projects
11
+ include Legion::Extensions::Tfe::Helpers::Client
12
+
13
+ def list_projects(organization:, url: nil, token: nil, **)
14
+ resp = connection(url: url, token: token).get("/api/v2/organizations/#{organization}/projects")
15
+ resp.body
16
+ end
17
+
18
+ def get_project(project_id:, url: nil, token: nil, **)
19
+ resp = connection(url: url, token: token).get("/api/v2/projects/#{project_id}")
20
+ resp.body
21
+ end
22
+
23
+ def create_project(organization:, name:, url: nil, token: nil, read_only: false, **)
24
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
25
+
26
+ payload = { data: { type: 'projects', attributes: { name: name } } }
27
+ resp = connection(url: url, token: token).post("/api/v2/organizations/#{organization}/projects", payload)
28
+ resp.body
29
+ end
30
+
31
+ def update_project(project_id:, url: nil, token: nil, read_only: false, **attrs)
32
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
33
+
34
+ payload = { data: { type: 'projects', attributes: attrs } }
35
+ resp = connection(url: url, token: token).patch("/api/v2/projects/#{project_id}", payload)
36
+ resp.body
37
+ end
38
+
39
+ def delete_project(project_id:, url: nil, token: nil, read_only: false, **)
40
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
41
+
42
+ connection(url: url, token: token).delete("/api/v2/projects/#{project_id}")
43
+ end
44
+
45
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
46
+ Legion::Extensions::Helpers.const_defined?(:Lex)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/errors'
4
+ require 'legion/extensions/tfe/helpers/client'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Tfe
9
+ module Runners
10
+ module Runs
11
+ include Legion::Extensions::Tfe::Helpers::Client
12
+
13
+ def list_runs(workspace_id:, url: nil, token: nil, page: 1, per_page: 20, **)
14
+ params = { 'page[number]' => page, 'page[size]' => per_page }
15
+ resp = connection(url: url, token: token).get("/api/v2/workspaces/#{workspace_id}/runs", params)
16
+ resp.body
17
+ end
18
+
19
+ def get_run(run_id:, url: nil, token: nil, **)
20
+ resp = connection(url: url, token: token).get("/api/v2/runs/#{run_id}")
21
+ resp.body
22
+ end
23
+
24
+ def create_run(workspace_id:, url: nil, token: nil, read_only: false, message: nil, auto_apply: false, **)
25
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
26
+
27
+ attrs = { auto_apply: auto_apply }
28
+ attrs[:message] = message if message
29
+ payload = {
30
+ data: {
31
+ type: 'runs',
32
+ attributes: attrs,
33
+ relationships: {
34
+ workspace: { data: { type: 'workspaces', id: workspace_id } }
35
+ }
36
+ }
37
+ }
38
+ resp = connection(url: url, token: token).post('/api/v2/runs', payload)
39
+ resp.body
40
+ end
41
+
42
+ def apply_run(run_id:, url: nil, token: nil, read_only: false, comment: nil, **)
43
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
44
+
45
+ payload = comment ? { comment: comment } : {}
46
+ resp = connection(url: url, token: token).post("/api/v2/runs/#{run_id}/actions/apply", payload)
47
+ resp.body
48
+ end
49
+
50
+ def discard_run(run_id:, url: nil, token: nil, read_only: false, comment: nil, **)
51
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
52
+
53
+ payload = comment ? { comment: comment } : {}
54
+ resp = connection(url: url, token: token).post("/api/v2/runs/#{run_id}/actions/discard", payload)
55
+ resp.body
56
+ end
57
+
58
+ def cancel_run(run_id:, url: nil, token: nil, read_only: false, comment: nil, **)
59
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
60
+
61
+ payload = comment ? { comment: comment } : {}
62
+ resp = connection(url: url, token: token).post("/api/v2/runs/#{run_id}/actions/cancel", payload)
63
+ resp.body
64
+ end
65
+
66
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
67
+ Legion::Extensions::Helpers.const_defined?(:Lex)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/helpers/client'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Tfe
8
+ module Runners
9
+ module StateVersions
10
+ include Legion::Extensions::Tfe::Helpers::Client
11
+
12
+ def list_state_versions(workspace_id:, url: nil, token: nil, **)
13
+ params = { 'filter[workspace][name]' => workspace_id }
14
+ resp = connection(url: url, token: token).get('/api/v2/state-versions', params)
15
+ resp.body
16
+ end
17
+
18
+ def get_state_version(state_version_id:, url: nil, token: nil, **)
19
+ resp = connection(url: url, token: token).get("/api/v2/state-versions/#{state_version_id}")
20
+ resp.body
21
+ end
22
+
23
+ def get_current_state_version(workspace_id:, url: nil, token: nil, **)
24
+ resp = connection(url: url, token: token).get("/api/v2/workspaces/#{workspace_id}/current-state-version")
25
+ resp.body
26
+ end
27
+
28
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
29
+ Legion::Extensions::Helpers.const_defined?(:Lex)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/errors'
4
+ require 'legion/extensions/tfe/helpers/client'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Tfe
9
+ module Runners
10
+ module VariableSets
11
+ include Legion::Extensions::Tfe::Helpers::Client
12
+
13
+ def list_variable_sets(organization:, url: nil, token: nil, **)
14
+ resp = connection(url: url, token: token).get("/api/v2/organizations/#{organization}/varsets")
15
+ resp.body
16
+ end
17
+
18
+ def get_variable_set(variable_set_id:, url: nil, token: nil, **)
19
+ resp = connection(url: url, token: token).get("/api/v2/varsets/#{variable_set_id}")
20
+ resp.body
21
+ end
22
+
23
+ def create_variable_set(organization:, name:, url: nil, token: nil, read_only: false, **attrs)
24
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
25
+
26
+ payload = { data: { type: 'varsets', attributes: { name: name }.merge(attrs) } }
27
+ resp = connection(url: url, token: token).post("/api/v2/organizations/#{organization}/varsets", payload)
28
+ resp.body
29
+ end
30
+
31
+ def update_variable_set(variable_set_id:, url: nil, token: nil, read_only: false, **attrs)
32
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
33
+
34
+ payload = { data: { type: 'varsets', attributes: attrs } }
35
+ resp = connection(url: url, token: token).patch("/api/v2/varsets/#{variable_set_id}", payload)
36
+ resp.body
37
+ end
38
+
39
+ def delete_variable_set(variable_set_id:, url: nil, token: nil, read_only: false, **)
40
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
41
+
42
+ connection(url: url, token: token).delete("/api/v2/varsets/#{variable_set_id}")
43
+ end
44
+
45
+ def list_varset_variables(variable_set_id:, url: nil, token: nil, **)
46
+ resp = connection(url: url, token: token).get("/api/v2/varsets/#{variable_set_id}/relationships/vars")
47
+ resp.body
48
+ end
49
+
50
+ def add_varset_variable(variable_set_id:, key:, value:, url: nil, token: nil, read_only: false, **attrs)
51
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
52
+
53
+ var_attrs = { key: key, value: value, category: 'terraform' }.merge(attrs)
54
+ payload = { data: { type: 'vars', attributes: var_attrs } }
55
+ resp = connection(url: url, token: token).post("/api/v2/varsets/#{variable_set_id}/relationships/vars",
56
+ payload)
57
+ resp.body
58
+ end
59
+
60
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
61
+ Legion::Extensions::Helpers.const_defined?(:Lex)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/errors'
4
+ require 'legion/extensions/tfe/helpers/client'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Tfe
9
+ module Runners
10
+ module Variables
11
+ include Legion::Extensions::Tfe::Helpers::Client
12
+
13
+ def list_variables(workspace_id:, url: nil, token: nil, **)
14
+ resp = connection(url: url, token: token).get("/api/v2/workspaces/#{workspace_id}/vars")
15
+ resp.body
16
+ end
17
+
18
+ # category: 'terraform' or 'env'
19
+ # sensitive: true/false
20
+ # hcl: true/false
21
+ def create_variable(workspace_id:, key:, value:, url: nil, token: nil, read_only: false, **attrs)
22
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
23
+
24
+ var_attrs = { key: key, value: value, category: 'terraform', sensitive: false, hcl: false }.merge(attrs)
25
+ payload = {
26
+ data: {
27
+ type: 'vars',
28
+ attributes: var_attrs,
29
+ relationships: {
30
+ workspace: { data: { type: 'workspaces', id: workspace_id } }
31
+ }
32
+ }
33
+ }
34
+ resp = connection(url: url, token: token).post('/api/v2/vars', payload)
35
+ resp.body
36
+ end
37
+
38
+ def update_variable(variable_id:, url: nil, token: nil, read_only: false, **attrs)
39
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
40
+
41
+ payload = { data: { type: 'vars', id: variable_id, attributes: attrs } }
42
+ resp = connection(url: url, token: token).patch("/api/v2/vars/#{variable_id}", payload)
43
+ resp.body
44
+ end
45
+
46
+ def delete_variable(variable_id:, url: nil, token: nil, read_only: false, **)
47
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
48
+
49
+ connection(url: url, token: token).delete("/api/v2/vars/#{variable_id}")
50
+ end
51
+
52
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
53
+ Legion::Extensions::Helpers.const_defined?(:Lex)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/errors'
4
+ require 'legion/extensions/tfe/helpers/client'
5
+
6
+ module Legion
7
+ module Extensions
8
+ module Tfe
9
+ module Runners
10
+ module Workspaces
11
+ include Legion::Extensions::Tfe::Helpers::Client
12
+
13
+ def list(organization:, url: nil, token: nil, page: 1, per_page: 20, search: nil, **)
14
+ params = { 'page[number]' => page, 'page[size]' => per_page }
15
+ params['search[name]'] = search if search
16
+ resp = connection(url: url, token: token).get("/api/v2/organizations/#{organization}/workspaces", params)
17
+ resp.body
18
+ end
19
+
20
+ def get(workspace_id:, url: nil, token: nil, **)
21
+ resp = connection(url: url, token: token).get("/api/v2/workspaces/#{workspace_id}")
22
+ resp.body
23
+ end
24
+
25
+ def create(organization:, name:, url: nil, token: nil, read_only: false, **attrs)
26
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
27
+
28
+ payload = { data: { type: 'workspaces', attributes: { name: name }.merge(attrs) } }
29
+ resp = connection(url: url, token: token).post("/api/v2/organizations/#{organization}/workspaces", payload)
30
+ resp.body
31
+ end
32
+
33
+ def update(workspace_id:, url: nil, token: nil, read_only: false, **attrs)
34
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
35
+
36
+ payload = { data: { type: 'workspaces', attributes: attrs } }
37
+ resp = connection(url: url, token: token).patch("/api/v2/workspaces/#{workspace_id}", payload)
38
+ resp.body
39
+ end
40
+
41
+ def delete(workspace_id:, url: nil, token: nil, read_only: false, **)
42
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
43
+
44
+ connection(url: url, token: token).delete("/api/v2/workspaces/#{workspace_id}")
45
+ end
46
+
47
+ def lock(workspace_id:, reason: nil, url: nil, token: nil, read_only: false, **)
48
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
49
+
50
+ payload = reason ? { reason: reason } : {}
51
+ resp = connection(url: url, token: token).post("/api/v2/workspaces/#{workspace_id}/actions/lock", payload)
52
+ resp.body
53
+ end
54
+
55
+ def unlock(workspace_id:, url: nil, token: nil, read_only: false, **)
56
+ raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
57
+
58
+ resp = connection(url: url, token: token).post("/api/v2/workspaces/#{workspace_id}/actions/unlock")
59
+ resp.body
60
+ end
61
+
62
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
63
+ Legion::Extensions::Helpers.const_defined?(:Lex)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Tfe
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/tfe/version'
4
+ require 'legion/extensions/tfe/errors'
5
+ require 'legion/extensions/tfe/helpers/client'
6
+ require 'legion/extensions/tfe/runners/workspaces'
7
+ require 'legion/extensions/tfe/runners/runs'
8
+ require 'legion/extensions/tfe/runners/plans'
9
+ require 'legion/extensions/tfe/runners/applies'
10
+ require 'legion/extensions/tfe/runners/variables'
11
+ require 'legion/extensions/tfe/runners/variable_sets'
12
+ require 'legion/extensions/tfe/runners/projects'
13
+ require 'legion/extensions/tfe/runners/organizations'
14
+ require 'legion/extensions/tfe/runners/state_versions'
15
+ require 'legion/extensions/tfe/runners/policy_sets'
16
+ require 'legion/extensions/tfe/client'
17
+
18
+ module Legion
19
+ module Extensions
20
+ module Tfe
21
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
22
+ end
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lex-tfe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Iverson
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ description: Connects LegionIO to Terraform Enterprise / HCP Terraform
27
+ email:
28
+ - matthewdiverson@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - ".github/workflows/ci.yml"
34
+ - ".rspec"
35
+ - ".rubocop.yml"
36
+ - CHANGELOG.md
37
+ - Gemfile
38
+ - LICENSE
39
+ - README.md
40
+ - lex-tfe.gemspec
41
+ - lib/legion/extensions/tfe.rb
42
+ - lib/legion/extensions/tfe/client.rb
43
+ - lib/legion/extensions/tfe/errors.rb
44
+ - lib/legion/extensions/tfe/helpers/client.rb
45
+ - lib/legion/extensions/tfe/runners/applies.rb
46
+ - lib/legion/extensions/tfe/runners/organizations.rb
47
+ - lib/legion/extensions/tfe/runners/plans.rb
48
+ - lib/legion/extensions/tfe/runners/policy_sets.rb
49
+ - lib/legion/extensions/tfe/runners/projects.rb
50
+ - lib/legion/extensions/tfe/runners/runs.rb
51
+ - lib/legion/extensions/tfe/runners/state_versions.rb
52
+ - lib/legion/extensions/tfe/runners/variable_sets.rb
53
+ - lib/legion/extensions/tfe/runners/variables.rb
54
+ - lib/legion/extensions/tfe/runners/workspaces.rb
55
+ - lib/legion/extensions/tfe/version.rb
56
+ homepage: https://github.com/LegionIO/lex-tfe
57
+ licenses:
58
+ - MIT
59
+ metadata:
60
+ homepage_uri: https://github.com/LegionIO/lex-tfe
61
+ source_code_uri: https://github.com/LegionIO/lex-tfe
62
+ documentation_uri: https://github.com/LegionIO/lex-tfe
63
+ changelog_uri: https://github.com/LegionIO/lex-tfe
64
+ bug_tracker_uri: https://github.com/LegionIO/lex-tfe/issues
65
+ rubygems_mfa_required: 'true'
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '3.4'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.6.9
81
+ specification_version: 4
82
+ summary: LEX TFE
83
+ test_files: []