a2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7b4e9ff951674c279e7ff7048a99344cce5b20dbf431b18f39c6d7a398fb62ba
4
+ data.tar.gz: 7897dbcb642d5ee458986d0e5dae1e467394c5a1a089c79eee50e003cda7c0a9
5
+ SHA512:
6
+ metadata.gz: 71f4f9287c22826592735d6bde1cb1e32f72de73efffabd45b53d98439f22a6d2957826ea025ca7755341621a596930137b3e8fd451e901f4c962ad3888e0b55
7
+ data.tar.gz: 547ec3d4af32a4c072870e994b945e78e742cc36fd76da89b81661c070b164dcae42bd0f4b83700854d00e596d333fbeb483a8e4b1f139a083a9cf6c59b67c2d
@@ -0,0 +1,25 @@
1
+ name: Publish Gem to rubygems.org
2
+
3
+ on: workflow_dispatch
4
+
5
+ jobs:
6
+ build:
7
+ name: Build + Publish
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - name: Set up Ruby 2.6
13
+ uses: actions/setup-ruby@v1
14
+ with:
15
+ ruby-version: 2.6.x
16
+ - name: Publish to RubyGems
17
+ run: |
18
+ mkdir -p $HOME/.gem
19
+ touch $HOME/.gem/credentials
20
+ chmod 0600 $HOME/.gem/credentials
21
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
22
+ gem build *.gemspec
23
+ gem push *.gem
24
+ env:
25
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
@@ -0,0 +1,25 @@
1
+ name: Unit Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ ruby-version: ['2.6']
16
+
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby-version }}
23
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
24
+ - name: Run tests
25
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in a2.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ a2 (0.1.0)
5
+ cmdparse
6
+ httparty
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ cmdparse (3.0.7)
12
+ httparty (0.18.1)
13
+ mime-types (~> 3.0)
14
+ multi_xml (>= 0.5.2)
15
+ mime-types (3.3.1)
16
+ mime-types-data (~> 3.2015)
17
+ mime-types-data (3.2021.0225)
18
+ minitest (5.14.2)
19
+ multi_xml (0.6.0)
20
+ rake (13.0.3)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ a2!
27
+ bundler (~> 1.17)
28
+ minitest (~> 5.0)
29
+ rake (>= 12.3.3)
30
+
31
+ BUNDLED WITH
32
+ 1.17.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 gscho
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # A2
2
+
3
+ ![build status](https://github.com/gscho/a2-cli/actions/workflows/run-rake.yml/badge.svg?branch=main)
4
+
5
+ This gem is a CLI for interacting with Chef Automate 2+ APIs.
6
+
7
+
8
+ ## Usage
9
+
10
+ ### Using from the command line
11
+
12
+ Install the CLI via rubygems:
13
+
14
+ $ gem install a2
15
+
16
+ The URL and auth token for your Chef Automate instance must be provided either via command line args or through environment variables. If both are provided, the command line args take precedence.
17
+
18
+ Environment variables:
19
+
20
+ ```
21
+ AUTOMATE_URL="https://automate.example.com"
22
+ AUTOMATE_TOKEN="my-token"
23
+ SSL_NO_VERIFY=true
24
+ ```
25
+
26
+ List the available commands:
27
+
28
+ $ a2 --help
29
+
30
+
31
+ ### Using as a library
32
+
33
+ You can also use it as a library by adding this line to your application's Gemfile:
34
+
35
+ ```ruby
36
+ gem 'a2'
37
+ ```
38
+
39
+ And then installing via bundler:
40
+
41
+ $ bundle install
42
+
43
+ Making requests:
44
+
45
+ ```
46
+ require 'a2'
47
+ client = A2::Client.new(automate_url: 'https://automate.example.com', automate_token: 'my-token', ssl_no_verify: true)
48
+ client.list_all_users
49
+ ```
50
+
51
+ ## License
52
+
53
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/a2.gemspec ADDED
@@ -0,0 +1,26 @@
1
+
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'a2/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'a2'
8
+ spec.version = A2::VERSION
9
+ spec.authors = ['gscho']
10
+ spec.email = ['greg.c.schofield@gmail.com']
11
+ spec.summary = %q{Write a short summary, because RubyGems requires one.}
12
+ spec.description = %q{Write a longer description or delete this line.}
13
+ spec.homepage = 'https://github.com/gscho/a2'
14
+ spec.license = 'MIT'
15
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
16
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ end
18
+ spec.bindir = 'bin'
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+ spec.add_development_dependency 'bundler', '~> 1.17'
22
+ spec.add_development_dependency 'rake', '>= 12.3.3'
23
+ spec.add_development_dependency 'minitest', '~> 5.0'
24
+ spec.add_dependency 'cmdparse'
25
+ spec.add_dependency 'httparty'
26
+ end
data/bin/a2 ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'a2'
3
+
4
+ include A2::Parser
5
+
6
+ parse!
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "a2"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/a2.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'cmdparse'
2
+ require 'httparty'
3
+ require 'json'
4
+
5
+ require_relative 'a2/version'
6
+ require_relative 'a2/client'
7
+ require_relative 'a2/mixins/approved'
8
+ require_relative 'a2/mixins/filtered'
9
+ require_relative 'a2/mixins/paginated'
10
+ require_relative 'a2/command'
11
+ require_relative 'a2/parser'
12
+
13
+ module A2
14
+ class Error < StandardError; end
15
+ end
data/lib/a2/client.rb ADDED
@@ -0,0 +1,56 @@
1
+ require_relative 'client/config_mgmt'
2
+ require_relative 'client/nodes'
3
+ require_relative 'client/teams'
4
+ require_relative 'client/users'
5
+
6
+ module A2
7
+ class Client
8
+ include A2::Client::ConfigMgmt
9
+ include A2::Client::Nodes
10
+ include A2::Client::Teams
11
+ include A2::Client::Users
12
+
13
+ def initialize(opts = {})
14
+ @automate_url = opts[:automate_url] || ENV['AUTOMATE_URL']
15
+ @automate_token = opts[:automate_token] || ENV['AUTOMATE_TOKEN']
16
+ @ssl_no_verify = opts[:ssl_no_verify] || ENV['AUTOMATE_SSL_NO_VERIFY'] || false
17
+
18
+ raise A2::Error, "Must provide the URL for Chef Automate" if @automate_url.nil?
19
+ raise A2::Error, "Must provide a token for Chef Automate" if @automate_token.nil?
20
+ end
21
+
22
+ def get(path)
23
+ response = HTTParty.get(File.join(@automate_url, path).to_s, {
24
+ verify: !@ssl_no_verify,
25
+ headers: {"api-token" => @automate_token},
26
+ })
27
+ JSON.parse(response.body)
28
+ end
29
+
30
+ def put(path, json)
31
+ response = HTTParty.put(File.join(@automate_url, path).to_s, {
32
+ verify: !@ssl_no_verify,
33
+ headers: {"api-token" => @automate_token},
34
+ body: json
35
+ })
36
+ JSON.parse(response.body)
37
+ end
38
+
39
+ def post(path, json)
40
+ response = HTTParty.post(File.join(@automate_url, path).to_s, {
41
+ verify: !@ssl_no_verify,
42
+ headers: {"api-token" => @automate_token},
43
+ body: json
44
+ })
45
+ JSON.parse(response.body)
46
+ end
47
+
48
+ def delete(path)
49
+ response = HTTParty.delete(File.join(@automate_url, path).to_s, {
50
+ verify: !@ssl_no_verify,
51
+ headers: {"api-token" => @automate_token},
52
+ })
53
+ JSON.parse(response.body)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ module A2
2
+ class Client
3
+ module ConfigMgmt
4
+ def list_all_checked_in_nodes(query_string = '')
5
+ get "/api/v0/cfgmgmt/nodes#{query_string}"
6
+ end
7
+
8
+ def list_all_missing_nodes_count(query_string = '?durations=3d&durations=1w&durations=2w&durations=1M&durations=3M')
9
+ get "/api/v0/cfgmgmt/stats/missing_node_duration_counts#{query_string}"
10
+ end
11
+
12
+ def list_all_node_status_counts(query_string = '')
13
+ get "/api/v0/cfgmgmt/stats/node_counts#{query_string}"
14
+ end
15
+
16
+ def list_all_organizations
17
+ get '/api/v0/cfgmgmt/organizations'
18
+ end
19
+
20
+ def show_attributes(node_id)
21
+ get "/api/v0/cfgmgmt/nodes/#{node_id}/attribute"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module A2
2
+ class Client
3
+ module Nodes
4
+ def get_managed_node(id)
5
+ get "/api/v0/nodes/id/#{id}"
6
+ end
7
+
8
+ def delete_managed_node(id)
9
+ delete "/api/v0/nodes/id/#{id}"
10
+ end
11
+
12
+ def search_managed_nodes(json)
13
+ post 'api/v0/nodes/search', json
14
+ end
15
+
16
+ def bulk_delete_managed_nodes_by_id(json)
17
+ post '/api/v0/nodes/delete/ids', json
18
+ end
19
+
20
+ def bulk_delete_managed_nodes_by_filter(json)
21
+ post '/api/v0/nodes/delete', json
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ module A2
2
+ class Client
3
+ module Teams
4
+ def list_all_teams
5
+ get '/apis/iam/v2/teams'
6
+ end
7
+
8
+ def get_team(id)
9
+ get "/apis/iam/v2/teams/#{id}"
10
+ end
11
+
12
+ def create_team(json)
13
+ post '/apis/iam/v2/teams', json
14
+ end
15
+
16
+ def update_team(id, json)
17
+ put "/apis/iam/v2/teams/#{id}", json
18
+ end
19
+
20
+ def delete_team(id)
21
+ delete "/apis/iam/v2/teams/#{id}"
22
+ end
23
+
24
+ def list_all_membership(team_id)
25
+ get "/apis/iam/v2/teams/#{team_id}/users"
26
+ end
27
+
28
+ def get_teams_by_membership(membership_id)
29
+ get "/apis/iam/v2/users/#{membership_id}/teams"
30
+ end
31
+
32
+ def add_membership(id)
33
+ post "/apis/iam/v2/teams/#{id}/users:add", json
34
+ end
35
+
36
+ def remove_membership(id, json)
37
+ post "/apis/iam/v2/teams/#{id}/users:remove", json
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,25 @@
1
+ module A2
2
+ class Client
3
+ module Users
4
+ def list_all_users
5
+ get '/apis/iam/v2/users'
6
+ end
7
+
8
+ def get_user(id)
9
+ get "/apis/iam/v2/users/#{id}"
10
+ end
11
+
12
+ def create_user(json)
13
+ post '/apis/iam/v2/users', json
14
+ end
15
+
16
+ def update_user(id, json)
17
+ put "/apis/iam/v2/users/#{id}", json
18
+ end
19
+
20
+ def delete_user(id)
21
+ delete "/apis/iam/v2/users/#{id}"
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/a2/command.rb ADDED
@@ -0,0 +1,8 @@
1
+ require_relative 'commands/config_mgmt'
2
+ require_relative 'commands/iam'
3
+ require_relative 'commands/node'
4
+
5
+ module A2
6
+ module Command
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../subcommands/config_mgmt'
2
+
3
+ module A2
4
+ module Command
5
+ class ConfigMgmt < CmdParse::Command
6
+ def initialize
7
+ super('cfgmgmt')
8
+ short_desc('Chef Infra config management commands')
9
+ long_desc('Chef Infra config management commands')
10
+ add_command(A2::Subcommand::ConfigMgmt::ListAllCheckedInNodes.new)
11
+ add_command(A2::Subcommand::ConfigMgmt::ListMissingNodesCount.new)
12
+ add_command(A2::Subcommand::ConfigMgmt::ListNodeStatusCounts.new)
13
+ add_command(A2::Subcommand::ConfigMgmt::ListOrganizations.new)
14
+ add_command(A2::Subcommand::ConfigMgmt::ShowAttributes.new)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../subcommands/user'
2
+ require_relative '../subcommands/team'
3
+
4
+ module A2
5
+ module Command
6
+ class IAM < CmdParse::Command
7
+ def initialize
8
+ super('iam')
9
+ short_desc('Identity access management commands')
10
+ add_command(A2::Subcommand::User::ListAll.new)
11
+ add_command(A2::Subcommand::User::Create.new)
12
+ add_command(A2::Subcommand::User::Get.new)
13
+ add_command(A2::Subcommand::User::Update.new)
14
+ add_command(A2::Subcommand::User::Delete.new)
15
+ add_command(A2::Subcommand::Team::ListAll.new)
16
+ add_command(A2::Subcommand::Team::Create.new)
17
+ add_command(A2::Subcommand::Team::Get.new)
18
+ add_command(A2::Subcommand::Team::Update.new)
19
+ add_command(A2::Subcommand::Team::Delete.new)
20
+ add_command(A2::Subcommand::Team::ListAllMembership.new)
21
+ add_command(A2::Subcommand::Team::AddMembership.new)
22
+ add_command(A2::Subcommand::Team::GetTeamsByMembership.new)
23
+ add_command(A2::Subcommand::Team::RemoveMembership.new)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../subcommands/node'
2
+
3
+ module A2
4
+ module Command
5
+ class Node < CmdParse::Command
6
+ def initialize
7
+ super('node')
8
+ short_desc('Node commands')
9
+ long_desc('Node commands')
10
+ add_command(A2::Subcommand::Node::Get.new)
11
+ add_command(A2::Subcommand::Node::Delete.new)
12
+ add_command(A2::Subcommand::Node::BulkDeleteById.new)
13
+ add_command(A2::Subcommand::Node::BulkDeleteByFilter.new)
14
+ add_command(A2::Subcommand::Node::Search.new)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module A2
2
+ module Approved
3
+ def initialize(name, opts = {})
4
+ super(name, takes_commands: false)
5
+ @opt = {}
6
+ options.on('-y', '--yes', 'Auto approve the deletion prompt.') do
7
+ @opt[:yes] = true
8
+ end
9
+ end
10
+
11
+ def ask_for_approval(message)
12
+ puts "Are you sure you want to #{message}?"
13
+ puts "Only 'yes' will be accepted to proceed:"
14
+ answer = $stdin.gets.chomp
15
+ abort('Operation cancelled') unless answer.eql?('yes')
16
+ answer
17
+ end
18
+
19
+ def with_approval(message, &block)
20
+ answer = 'yes'
21
+ answer = ask_for_approval(message) unless @opt[:yes]
22
+ block.call if answer.eql?('yes')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,76 @@
1
+ module A2
2
+ module Filtered
3
+ def initialize(name, opts = {})
4
+ set_custom_opts!(opts)
5
+ super(name, opts)
6
+ set_filter_optparse_options!(options)
7
+ end
8
+
9
+ def set_custom_opts!(opts)
10
+ @opt ||= {}
11
+ @opt[:query_filter] = opts.delete(:query_filter) || false
12
+ end
13
+
14
+ def set_filter_optparse_options!(options)
15
+ options.on('-f', '--filters FILTERS', 'A comma-separated list of filters.') do |filters|
16
+ @opt[:filters] = filters
17
+ end
18
+ # Only available for POST body filters
19
+ options.on('-j', '--json-file FILE', 'Path to a json file containing a filter payload.') do |file|
20
+ @opt[:json_file] = file
21
+ end unless query_filter?
22
+ # Only available for query parameter filters
23
+ options.on('-S', '--start START', 'Earliest most recent check-in node information to return. Formatted iso8601 (YYYY-MM-DD\'T\'HH:mm:ssZ)') do |start|
24
+ @opt[:start] = start
25
+ end if query_filter?
26
+ options.on('-E', '--end END', 'Latest most recent check-in node information to return. Formatted iso8601 (YYYY-MM-DD\'T\'HH:mm:ssZ)') do |end_arg|
27
+ @opt[:end] = end_arg
28
+ end if query_filter?
29
+ end
30
+
31
+ def query_filter?
32
+ @opt[:query_filter]
33
+ end
34
+
35
+ def parse_filters(filters)
36
+ parsed_filters = []
37
+ filters.split(',').each do |f|
38
+ k,v = f.split(':')
39
+ parsed_filters << {
40
+ 'key' => k,
41
+ 'values' => [v]
42
+ }
43
+ end
44
+ parsed_filters
45
+ end
46
+
47
+ def generate_json_filters
48
+ if @opt[:json_file]
49
+ file_contents = File.read(@opt[:json_file])
50
+ JSON.parse(file_contents).to_json
51
+ elsif @opt[:filters]
52
+ @opt[:filters] = parse_filters(@opt[:filters])
53
+ @opt.to_json
54
+ else
55
+ command_parser.main_command.commands['help'].execute(*command_parser.current_command.command_chain.map(&:name))
56
+ abort('Must provide one of either "--json-file" or "--filters"')
57
+ end
58
+ end
59
+
60
+ def generate_query_filters
61
+ query = []
62
+ @opt[:filters].split(',').each do |filter|
63
+ query << "filter=#{filter}"
64
+ end if @opt[:filters]
65
+ query << "start=#{@opt[:start]}" if @opt[:start]
66
+ query << "end=#{@opt[:end]}" if @opt[:end]
67
+ query_string = query.join('&')
68
+ query_string.prepend('?')
69
+ end
70
+
71
+ def with_filter_query(&block)
72
+ query_string = generate_query_filters
73
+ yield(query_string)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,48 @@
1
+ module A2
2
+ module Paginated
3
+ include A2::Filtered
4
+ def initialize(name, opts = {})
5
+ set_custom_opts!(opts)
6
+ super(name, opts)
7
+ @opt = {
8
+ order: 'ASC'
9
+ }
10
+ options.on('-o', '--order ORDER', 'Return the results in ascending or descending order. Default is "ASC".') do |order|
11
+ @opt[:order] = order
12
+ end
13
+ options.on('-p', '--page PAGE', Integer, 'Starting page for the results. Default is 0') do |page|
14
+ @opt[:page] = page
15
+ end
16
+ options.on('-P', '--per-page PER_PAGE', Integer, 'The number of results on each page. Default is 100') do |per_page|
17
+ @opt[:per_page] = per_page
18
+ end
19
+ options.on('-s', '--sort SORT', 'Sort the results on a specific field.') do |sort|
20
+ @opt[:sort] = sort
21
+ end
22
+ set_filter_optparse_options!(options)
23
+ end
24
+
25
+ def generate_paginated_json_filters
26
+ generate_json_filters
27
+ end
28
+
29
+ def with_paginated_filter_json(&block)
30
+ json = generate_paginated_json_filters
31
+ yield(json)
32
+ end
33
+
34
+ def generate_paginated_query_string
35
+ query = generate_query_filters
36
+ query << "&pagination.page=#{@opt[:page]}" if @opt[:page]
37
+ query << "&pagination.page=#{@opt[:per_page]}" if @opt[:per_page]
38
+ query << "&sorting.field=#{@opt[:sort]}" if @opt[:sort]
39
+ query << "&sorting.order=#{@opt[:order]}" if @opt[:order]
40
+ query
41
+ end
42
+
43
+ def with_paginated_filter_query(&block)
44
+ query_string = generate_paginated_query_string
45
+ yield(query_string)
46
+ end
47
+ end
48
+ end
data/lib/a2/parser.rb ADDED
@@ -0,0 +1,25 @@
1
+ module A2
2
+ module Parser
3
+ def parse!
4
+ parser = CmdParse::CommandParser.new(handle_exceptions: true)
5
+ parser.main_options.version = A2::VERSION
6
+ parser.add_command(CmdParse::HelpCommand.new)
7
+ parser.add_command(CmdParse::VersionCommand.new)
8
+ parser.add_command(A2::Command::ConfigMgmt.new)
9
+ parser.add_command(A2::Command::IAM.new)
10
+ parser.add_command(A2::Command::Node.new)
11
+ parser.global_options do |opt|
12
+ opt.on("-u", "--automate-url [url]", "The Chef Automate URL") do |url|
13
+ parser.data[:automate_url] = url
14
+ end
15
+ opt.on("-t", "--automate-token [token]", "The Chef Automate API token") do |token|
16
+ parser.data[:automate_token] = token
17
+ end
18
+ opt.on("-N", "--ssl-no-verify", "Disable SSL verification") do
19
+ parser.data[:ssl_no_verify] = true
20
+ end
21
+ end
22
+ parser.parse
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ module A2
2
+ module Subcommand
3
+ module ConfigMgmt
4
+ class ListAllCheckedInNodes < CmdParse::Command
5
+ include A2::Paginated
6
+ def initialize
7
+ super('list-nodes', takes_commands: false, query_filter: true)
8
+ end
9
+
10
+ def execute
11
+ with_filter_query do |query_string|
12
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_checked_in_nodes(query_string))
13
+ end
14
+ end
15
+ end
16
+ class ListMissingNodesCount < CmdParse::Command
17
+ def initialize
18
+ super('list-missing-nodes', takes_commands: false)
19
+ @opt = {}
20
+ options.on('-d', '--durations DURATIONS', "A comma-separated list of durations. A valid duration is any number zero or greater with one of these characters 'h', 'd', 'w', or 'M'. 'h' is hours 'd' is days 'w' is weeks 'M' is months Will contain one or many.") do |durations|
21
+ @opt[:durations] = durations.split(',').join('&durations=').prepend('?durations=')
22
+ end
23
+ end
24
+
25
+ def execute
26
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_missing_nodes_count(@opt[:durations]))
27
+ end
28
+ end
29
+ class ListNodeStatusCounts < CmdParse::Command
30
+ include A2::Filtered
31
+ def initialize
32
+ super('list-node-status-counts', takes_commands: false, query_filter: true)
33
+ end
34
+
35
+ def execute
36
+ with_filter_query do |query_filter|
37
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_node_status_counts(query_filter))
38
+ end
39
+ end
40
+ end
41
+ class ListOrganizations < CmdParse::Command
42
+ def initialize
43
+ super('list-orgs', takes_commands: false)
44
+ end
45
+
46
+ def execute
47
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_organizations)
48
+ end
49
+ end
50
+ class ShowAttributes < CmdParse::Command
51
+ def initialize
52
+ super('show-attributes', takes_commands: false)
53
+ end
54
+
55
+ def execute(node_id)
56
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).show_attributes(node_id))
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,70 @@
1
+ module A2
2
+ module Subcommand
3
+ module Node
4
+ class Get < CmdParse::Command
5
+ def initialize
6
+ super('get', takes_commands: false)
7
+ end
8
+
9
+ def execute(id)
10
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).get_managed_node(id))
11
+ end
12
+ end
13
+ class Search < CmdParse::Command
14
+ include A2::Paginated
15
+
16
+ def initialize
17
+ super('search', takes_commands: false)
18
+ end
19
+
20
+ def execute
21
+ with_paginated_filter_json do |json|
22
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).search_managed_nodes(json))
23
+ end
24
+ end
25
+ end
26
+ class Delete < CmdParse::Command
27
+ include A2::Approved
28
+
29
+ def initialize
30
+ super('delete', takes_commands: false)
31
+ end
32
+
33
+ def execute(id)
34
+ with_approval("delete node #{id}") do
35
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).delete_managed_node(id))
36
+ end
37
+ end
38
+ end
39
+ class BulkDeleteById < CmdParse::Command
40
+ include A2::Approved
41
+
42
+ def initialize
43
+ super('bulk-delete-by-ids', takes_commands: false)
44
+ end
45
+
46
+ def execute(ids)
47
+ with_approval("delete nodes") do
48
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).bulk_delete_managed_nodes_by_id(id))
49
+ end
50
+ end
51
+ end
52
+ class BulkDeleteByFilter < CmdParse::Command
53
+ include A2::Approved
54
+ include A2::Paginated
55
+
56
+ def initialize
57
+ super('bulk-delete-by-filter', takes_commands: false)
58
+ end
59
+
60
+ def execute
61
+ with_paginated_filter_json do |json|
62
+ with_approval("delete nodes using filter") do
63
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).bulk_delete_managed_nodes_by_filter(json))
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,100 @@
1
+ module A2
2
+ module Subcommand
3
+ module Team
4
+ def self.generate_team_json(id, name, project_ids)
5
+ {
6
+ 'id' => id,
7
+ 'name' => name,
8
+ 'projects' => project_ids.split(',')
9
+ }.to_json
10
+ end
11
+ class ListAll < CmdParse::Command
12
+ def initialize
13
+ super('list-teams', takes_commands: false)
14
+ end
15
+
16
+ def execute
17
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_teams)
18
+ end
19
+ end
20
+ class Create < CmdParse::Command
21
+ def initialize
22
+ super('create-team', takes_commands: false)
23
+ end
24
+
25
+ def execute(id, name, project_ids = '')
26
+ json = Team.generate_team_json(id, name, project_ids)
27
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).create_team(json))
28
+ end
29
+ end
30
+ class Get < CmdParse::Command
31
+ def initialize
32
+ super('get-team', takes_commands: false)
33
+ end
34
+
35
+ def execute(id)
36
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).get_team(id))
37
+ end
38
+ end
39
+ class Update < CmdParse::Command
40
+ def initialize
41
+ super('update-team', takes_commands: false)
42
+ end
43
+
44
+ def execute(id, name, project_ids = '')
45
+ json = Team.generate_team_json(id, name, project_ids)
46
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).update_team(id, json))
47
+ end
48
+ end
49
+ class Delete < CmdParse::Command
50
+ include A2::Approved
51
+
52
+ def initialize
53
+ super('delete-team', takes_commands: false)
54
+ end
55
+
56
+ def execute(id)
57
+ with_approval("delete team #{id}") do
58
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).delete_team(id))
59
+ end
60
+ end
61
+ end
62
+ class ListAllMembership < CmdParse::Command
63
+ def initialize
64
+ super('list-team-members', takes_commands: false)
65
+ end
66
+
67
+ def execute(team_id)
68
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_membership(team_id))
69
+ end
70
+ end
71
+ class AddMembership < CmdParse::Command
72
+ def initialize
73
+ super('add-membership', takes_commands: false)
74
+ end
75
+
76
+ def execute
77
+
78
+ end
79
+ end
80
+ class GetTeamsByMembership < CmdParse::Command
81
+ def initialize
82
+ super('get-teams-by-membership', takes_commands: false)
83
+ end
84
+
85
+ def execute(membership_id)
86
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).get_teams_by_membership(membership_id))
87
+ end
88
+ end
89
+ class RemoveMembership < CmdParse::Command
90
+ def initialize
91
+ super('remove-membership', takes_commands: false)
92
+ end
93
+
94
+ def execute
95
+
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,63 @@
1
+ module A2
2
+ module Subcommand
3
+ module User
4
+ class ListAll < CmdParse::Command
5
+ def initialize
6
+ super('list-users', takes_commands: false)
7
+ end
8
+
9
+ def execute
10
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).list_all_users)
11
+ end
12
+ end
13
+ class Create < CmdParse::Command
14
+ def initialize
15
+ super('create-user', takes_commands: false)
16
+ end
17
+
18
+ def execute(id, display_name = id, password)
19
+ json = {
20
+ 'id' => id,
21
+ 'name' => display_name,
22
+ 'password' => password
23
+ }.to_json
24
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).create_user(json))
25
+ end
26
+ end
27
+ class Get < CmdParse::Command
28
+ def initialize
29
+ super('get-user', takes_commands: false)
30
+ end
31
+
32
+ def execute(id)
33
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).get_user(id))
34
+ end
35
+ end
36
+ class Update < CmdParse::Command
37
+ def initialize
38
+ super('update-user', takes_commands: false)
39
+ end
40
+
41
+ def execute(id, display_name, password = nil)
42
+ body = { 'name' => display_name}
43
+ body['password'] = password unless password.nil?
44
+
45
+ json = body.to_json
46
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).update_user(id, json))
47
+ end
48
+ end
49
+ class Delete < CmdParse::Command
50
+ include A2::Approved
51
+ def initialize
52
+ super('delete-user', takes_commands: false)
53
+ end
54
+
55
+ def execute(id)
56
+ with_approval("delete user #{id}") do
57
+ puts JSON.pretty_generate(A2::Client.new(command_parser.data).delete_user(id))
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/a2/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module A2
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: a2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - gscho
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-03-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 12.3.3
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 12.3.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: cmdparse
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: httparty
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Write a longer description or delete this line.
84
+ email:
85
+ - greg.c.schofield@gmail.com
86
+ executables:
87
+ - a2
88
+ - console
89
+ - setup
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".github/workflows/gem-push.yml"
94
+ - ".github/workflows/run-rake.yml"
95
+ - ".gitignore"
96
+ - Gemfile
97
+ - Gemfile.lock
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - a2.gemspec
102
+ - bin/a2
103
+ - bin/console
104
+ - bin/setup
105
+ - lib/a2.rb
106
+ - lib/a2/client.rb
107
+ - lib/a2/client/config_mgmt.rb
108
+ - lib/a2/client/nodes.rb
109
+ - lib/a2/client/teams.rb
110
+ - lib/a2/client/users.rb
111
+ - lib/a2/command.rb
112
+ - lib/a2/commands/config_mgmt.rb
113
+ - lib/a2/commands/iam.rb
114
+ - lib/a2/commands/node.rb
115
+ - lib/a2/mixins/approved.rb
116
+ - lib/a2/mixins/filtered.rb
117
+ - lib/a2/mixins/paginated.rb
118
+ - lib/a2/parser.rb
119
+ - lib/a2/subcommands/config_mgmt.rb
120
+ - lib/a2/subcommands/node.rb
121
+ - lib/a2/subcommands/team.rb
122
+ - lib/a2/subcommands/user.rb
123
+ - lib/a2/version.rb
124
+ homepage: https://github.com/gscho/a2
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubygems_version: 3.0.3
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Write a short summary, because RubyGems requires one.
147
+ test_files: []