Skip to content

Commit

Permalink
Add firewall rule management to cb network command.
Browse files Browse the repository at this point in the history
These changes move firewall rule management under `cb network` and
deprecate the `cb firewall` rule command. This comes as part of a
request to be able to update firewall rules more completely as described
in #161.

The following commands are now supported:

* `cb network add-firewall-rule`
* `cb network list-firewall-rules`
* `cb network remove-firewall-rule`
* `cb network update-firewall-rule`

Each command requires the specification of the network to peform the
operation. As well, the shell completion logic has been updated to
suggest the list of available networks and firewall rule when
applicable.
  • Loading branch information
abrightwell committed May 29, 2024
1 parent b5ce6f8 commit 4eefc52
Show file tree
Hide file tree
Showing 12 changed files with 698 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ jobs:
asset_name: cb-v${{ steps.version.outputs.version }}_macos_amd64.zip
asset_content_type: application/zip

- name: Update release zip from macos arm64
- name: Upload release zip from macos arm64
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `cb network` now manages firewall rules and supports the following
subcommands: `add-firewall-rule`, `list-firewall-rules`,
`remove-firewall-rule` and `update-firewall-rule`

### Deprecated
- `cb firewall` deprecated in favor of `cb network`.

## [3.5.1] - 2024-05-09
### Fixed
Expand Down
39 changes: 39 additions & 0 deletions spec/cb/completion_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ private class CompletionTestClient < CB::Client
[Factory.team(name: "my team", role: "manager")]
end

def get_networks(team)
[Factory.network]
end

def get_firewall_rules(id)
[Factory.firewall_rule(id: "f1", rule: "1.2.3.4/32"), Factory.firewall_rule(id: "f2", rule: "4.5.6.7/24")]
end
Expand Down Expand Up @@ -662,9 +666,44 @@ Spectator.describe CB::Completion do
expect(result).to have_option "network"

result = parse("cb network ")
expect(result).to have_option "add-firewall-rule"
expect(result).to have_option "info"
expect(result).to have_option "list"
expect(result).to have_option "list-firewall-rules"
expect(result).to have_option "remove-firewall-rule"
expect(result).to have_option "update-firewall-rule"

# Network Firewall Rule Management
result = parse("cb network add-firewall-rule")
expect(result).to have_option "--format"
expect(result).to have_option "--network"
expect(result).to have_option "--rule"

result = parse("cb network list-firewall-rules")
expect(result).to have_option "--format"
expect(result).to have_option "--network"

result = parse("cb network remove-firewall-rule")
expect(result).to have_option "--format"
expect(result).to have_option "--network"
expect(result).to_not have_option "--firewall-rule"

result = parse("cb network remove-firewall-rule --network abc ")
expect(result).to have_option "--firewall-rule"

result = parse("cb network update-firewall-rule")
expect(result).to have_option "--description"
expect(result).to_not have_option "--firewall-rule"
expect(result).to have_option "--format"
expect(result).to have_option "--network"
expect(result).to have_option "--rule"

result = parse("cb network update-firewall-rule --network abc ")
expect(result).to have_option "--description"
expect(result).to have_option "--firewall-rule"
expect(result).to have_option "--rule"

# Network Management
result = parse("cb network info ")
expect(result).to have_option "--network"
expect(result).to have_option "--format"
Expand Down
284 changes: 284 additions & 0 deletions spec/cb/firewall_rule_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
require "../spec_helper"

Spectator.describe FirewallRuleAdd do
subject(action) { described_class.new client: client, output: IO::Memory.new }

mock_client

let(network) { Factory.network }

describe "#validate" do
it "ensures required arguments are present" do
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.network_id = network.id
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.rule = "0.0.0.0/0"
expect(&.validate).to be_true
end
end

describe "#call" do
before_each {
action.output = IO::Memory.new
action.network_id = network.id
action.rule = Factory.firewall_rule.rule

expect(client).to receive(:create_firewall_rule).and_return Factory.firewall_rule
}

it "outputs table with header" do
action.call

expected = <<-EXPECTED
ID Rule Description
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs table without header" do
action.no_header = true
action.call

expected = <<-EXPECTED
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs json" do
action.format = Format::JSON
action.call

expected = <<-EXPECTED
{
"firewall_rules": [
{
"id": "shofthj3fzaipie44lt6a5i3de",
"description": "Example Description",
"rule": "1.2.3.0/24"
}
]
}
EXPECTED

expect(&.output.to_s).to look_like expected
end
end
end

Spectator.describe FirewallRuleList do
subject(action) { described_class.new client: client, output: IO::Memory.new }

mock_client

let(network) { Factory.network }

describe "#validate" do
it "ensures required arguments are present" do
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.network_id = network.id
expect(&.validate).to be_true
end
end

describe "#call" do
before_each {
action.output = IO::Memory.new
action.network_id = network.id

expect(client).to receive(:get_firewall_rules).and_return [Factory.firewall_rule]
}

it "outputs table with header" do
action.call

expected = <<-EXPECTED
ID Rule Description
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs table without header" do
action.no_header = true
action.call

expected = <<-EXPECTED
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs json" do
action.format = Format::JSON
action.call

expected = <<-EXPECTED
{
"firewall_rules": [
{
"id": "shofthj3fzaipie44lt6a5i3de",
"description": "Example Description",
"rule": "1.2.3.0/24"
}
]
}
EXPECTED

expect(&.output.to_s).to look_like expected
end
end
end

Spectator.describe FirewallRuleRemove do
subject(action) { described_class.new client: client, output: IO::Memory.new }

mock_client

let(network) { Factory.network }
let(firewall_rule) { Factory.firewall_rule }

describe "#validate" do
it "ensures required arguments are present" do
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.network_id = network.id
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.firewall_rule_id = firewall_rule.id
expect(&.validate).to be_true
end
end

describe "#call" do
before_each {
action.output = IO::Memory.new
action.network_id = network.id
action.firewall_rule_id = firewall_rule.id

expect(client).to receive(:destroy_firewall_rule).and_return Factory.firewall_rule
}

it "outputs table with header" do
action.call

expected = <<-EXPECTED
ID Rule Description
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs table without header" do
action.no_header = true
action.call

expected = <<-EXPECTED
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs json" do
action.format = Format::JSON
action.call

expected = <<-EXPECTED
{
"firewall_rules": [
{
"id": "shofthj3fzaipie44lt6a5i3de",
"description": "Example Description",
"rule": "1.2.3.0/24"
}
]
}
EXPECTED

expect(&.output.to_s).to look_like expected
end
end
end

Spectator.describe FirewallRuleUpdate do
subject(action) { described_class.new client: client, output: IO::Memory.new }

mock_client

let(network) { Factory.network }
let(firewall_rule) { Factory.firewall_rule }

describe "#validate" do
it "ensures required arguments are present" do
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.network_id = network.id
expect(&.validate).to raise_error Program::Error, /Missing required argument/

action.firewall_rule_id = firewall_rule.id
expect(&.validate).to be_true
end
end

describe "#call" do
before_each {
action.output = IO::Memory.new
action.network_id = network.id
action.firewall_rule_id = firewall_rule.id
action.rule = Factory.firewall_rule.rule

expect(client).to receive(:update_firewall_rule).and_return Factory.firewall_rule
}

it "outputs table with header" do
action.call

expected = <<-EXPECTED
ID Rule Description
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs table without header" do
action.no_header = true
action.call

expected = <<-EXPECTED
shofthj3fzaipie44lt6a5i3de 1.2.3.0/24 Example Description
EXPECTED

expect(&.output.to_s).to look_like expected
end

it "outputs json" do
action.format = Format::JSON
action.call

expected = <<-EXPECTED
{
"firewall_rules": [
{
"id": "shofthj3fzaipie44lt6a5i3de",
"description": "Example Description",
"rule": "1.2.3.0/24"
}
]
}
EXPECTED

expect(&.output.to_s).to look_like expected
end
end
end
5 changes: 3 additions & 2 deletions spec/support/factory.cr
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ module Factory

def firewall_rule(**params)
params = {
id: "shofthj3fzaipie44lt6a5i3de",
rule: "1.2.3.0/24",
description: "Example Description",
id: "shofthj3fzaipie44lt6a5i3de",
rule: "1.2.3.0/24",
}.merge(params)
CB::Model::FirewallRule.new **params
end
Expand Down
Loading

0 comments on commit 4eefc52

Please sign in to comment.