Skip to content

Commit

Permalink
Merge pull request #156 from bibendi/feature/k8s
Browse files Browse the repository at this point in the history
Add kubectl support
  • Loading branch information
bibendi authored Nov 4, 2022
2 parents af73f75 + a38425d commit b77a046
Show file tree
Hide file tree
Showing 14 changed files with 435 additions and 93 deletions.
45 changes: 40 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ After that we can type commands without `dip` prefix. For example:
<run-command> *any-args
compose *any-compose-arg
up <service>
build
down
ktl *any-kubectl-arg
provision
```

Expand Down Expand Up @@ -81,10 +80,11 @@ Also, you can check out examples at the top.

```yml
# Required minimum dip version
version: '7.1'
version: '7.5'

environment:
COMPOSE_EXT: development
STAGE: "staging"

compose:
files:
Expand All @@ -93,6 +93,9 @@ compose:
- docker/docker-compose.$DIP_OS.yml
project_name: bear

kubectl:
namespace: rocket-$STAGE

interaction:
shell:
description: Open the Bash shell in app's container
Expand Down Expand Up @@ -142,6 +145,22 @@ interaction:
default_args: db_dev
command: psql -h pg -U postgres

k:
description: Run commands in Kubernetes cluster
pod: svc/rocket-app:app-container
entrypoint: /env-entrypoint
subcommands:
bash:
description: Get a shell to the running container
command: /bin/bash
rails:
description: Run Rails commands
command: bundle exec rails
kafka-topics:
description: Manage Kafka topics
pod: svc/rocket-kafka
command: kafka-topics.sh --zookeeper zookeeper:2181

setup_key:
description: Copy key
service: app
Expand Down Expand Up @@ -201,7 +220,13 @@ returned is `/app/sub-project-dir`.

Run commands defined within the `interaction` section of dip.yml

By default, a command will be executed using [`docker compose`](https://docs.docker.com/compose/install/) command. If you are still using `docker-compose` binary (i.e., prior to Compose V2 changes), a command would be run through it. You can disable using of Compose V2 by passing an environment variable `DIP_COMPOSE_V2=false dip run`.
A command will be executed by specified runner. Dip has three types of them:

- `docker-compose` runner — used when the `service` option is defined.
- `kubectl` runner — used when the `pod` option is defined.
- `local` runner — used when the previous ones are not defined.

If you are still using `docker-compose` binary (i.e., prior to Compose V2 changes), a command would be run through it. You can disable using of Compose V2 by passing an environment variable `DIP_COMPOSE_V2=false dip run`.

```sh
dip run rails c
Expand Down Expand Up @@ -254,14 +279,24 @@ Run commands each by each from `provision` section of dip.yml

### dip compose

Run docker-compose commands that are configured according to the application's dip.yml :
Run docker-compose commands that are configured according to the application's dip.yml:

```sh
dip compose COMMAND [OPTIONS]
dip compose up -d redis
```

### dip ktl

Run kubectl commands that are configured according to the application's dip.yml:

```sh
dip ktl COMMAND [OPTIONS]
STAGE=some dip ktl get pods
```

### dip ssh

Runs ssh-agent container based on https://github.com/whilp/ssh-agent with your ~/.ssh/id_rsa.
Expand Down
16 changes: 13 additions & 3 deletions lib/dip/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def start(argv)
end
end

stop_on_unknown_option! :run
stop_on_unknown_option! :run, :ktl

desc "version", "dip version"
def version
Expand Down Expand Up @@ -82,7 +82,13 @@ def down(*argv)
end
end

desc "run [OPTIONS] CMD [ARGS]", "Run configured command in a docker-compose service. `run` prefix may be omitted"
desc "ktl CMD [OPTIONS]", "Run kubectl commands"
def ktl(*argv)
require_relative "commands/kubectl"
Dip::Commands::Kubectl.new(*argv).execute
end

desc "run [OPTIONS] CMD [ARGS]", "Run configured command (`run` prefix may be omitted)"
method_option :publish, aliases: "-p", type: :string, repeatable: true,
desc: "Publish a container's port(s) to the host"
method_option :help, aliases: "-h", type: :boolean, desc: "Display usage information"
Expand All @@ -91,7 +97,11 @@ def run(*argv)
invoke :help, ["run"]
else
require_relative "commands/run"
Dip::Commands::Run.new(*argv, publish: options[:publish]).execute

Dip::Commands::Run.new(
*argv,
**options.to_h.transform_keys!(&:to_sym)
).execute
end
end

Expand Down
34 changes: 34 additions & 0 deletions lib/dip/commands/kubectl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

require_relative "../command"

module Dip
module Commands
class Kubectl < Dip::Command
attr_reader :argv, :config

def initialize(*argv)
@argv = argv
@config = ::Dip.config.kubectl || {}
end

def execute
k_argv = cli_options + argv

exec_program("kubectl", k_argv)
end

private

def cli_options
%i[namespace].flat_map do |name|
next unless (value = config[name])
next unless value.is_a?(String)

value = ::Dip.env.interpolate(value).delete_suffix("-")
["--#{name.to_s.tr("_", "-")}", value]
end.compact
end
end
end
end
86 changes: 20 additions & 66 deletions lib/dip/commands/run.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
require_relative "../../../lib/dip/run_vars"
require_relative "../command"
require_relative "../interaction_tree"
require_relative "compose"
require_relative "runners/local_runner"
require_relative "runners/docker_compose_runner"
require_relative "runners/kubectl_runner"

require_relative "kubectl"

module Dip
module Commands
class Run < Dip::Command
def initialize(cmd, *argv, publish: nil)
@publish = publish
def initialize(cmd, *argv, **options)
@options = options

@command, @argv = InteractionTree
.new(Dip.config.interaction)
Expand All @@ -22,75 +26,25 @@ def initialize(cmd, *argv, publish: nil)
end

def execute
if command[:service].nil?
exec_program(command[:command], get_args, shell: command[:shell])
else
Dip::Commands::Compose.new(
command[:compose][:method],
*compose_arguments,
shell: command[:shell]
).execute
end
lookup_runner
.new(command, argv, **options)
.execute
end

private

attr_reader :command, :argv, :publish

def compose_arguments
compose_argv = command[:compose][:run_options].dup

if command[:compose][:method] == "run"
compose_argv.concat(run_vars)
compose_argv.concat(published_ports)
compose_argv << "--rm"
end

compose_argv << command.fetch(:service)

unless (cmd = command[:command]).empty?
if command[:shell]
compose_argv << cmd
else
compose_argv.concat(cmd.shellsplit)
end
end

compose_argv.concat(get_args)

compose_argv
end

def run_vars
run_vars = Dip::RunVars.env
return [] unless run_vars

run_vars.map { |k, v| ["-e", "#{k}=#{Shellwords.escape(v)}"] }.flatten
end

def published_ports
if publish.respond_to?(:each)
publish.map { |p| "--publish=#{p}" }
else
[]
end
end
attr_reader :command, :argv, :options

def get_args
if argv.any?
if command[:shell]
[argv.shelljoin]
else
Array(argv)
end
elsif !(default_args = command[:default_args]).empty?
if command[:shell]
default_args.shellsplit
else
Array(default_args)
end
def lookup_runner
if (runner = command[:runner])
camelized_runner = runner.split("_").collect(&:capitalize).join
Runners.const_get("#{camelized_runner}Runner")
elsif command[:service]
Runners::DockerComposeRunner
elsif command[:pod]
Runners::KubectlRunner
else
[]
Runners::LocalRunner
end
end
end
Expand Down
41 changes: 41 additions & 0 deletions lib/dip/commands/runners/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

module Dip
module Commands
module Runners
class Base
def initialize(command, argv, **options)
@command = command
@argv = argv
@options = options
end

def execute
raise NotImplementedError
end

private

attr_reader :command, :argv, :options

def command_args
if argv.any?
if command[:shell]
[argv.shelljoin]
else
Array(argv)
end
elsif !(default_args = command[:default_args]).empty?
if command[:shell]
default_args.shellsplit
else
Array(default_args)
end
else
[]
end
end
end
end
end
end
63 changes: 63 additions & 0 deletions lib/dip/commands/runners/docker_compose_runner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

require_relative "base"
require_relative "../compose"

module Dip
module Commands
module Runners
class DockerComposeRunner < Base
def execute
Commands::Compose.new(
command[:compose][:method],
*compose_arguments,
shell: command[:shell]
).execute
end

private

def compose_arguments
compose_argv = command[:compose][:run_options].dup

if command[:compose][:method] == "run"
compose_argv.concat(run_vars)
compose_argv.concat(published_ports)
compose_argv << "--rm"
end

compose_argv << command.fetch(:service)

unless (cmd = command[:command]).empty?
if command[:shell]
compose_argv << cmd
else
compose_argv.concat(cmd.shellsplit)
end
end

compose_argv.concat(command_args)

compose_argv
end

def run_vars
run_vars = Dip::RunVars.env
return [] unless run_vars

run_vars.map { |k, v| ["-e", "#{k}=#{Shellwords.escape(v)}"] }.flatten
end

def published_ports
publish = options[:publish]

if publish.respond_to?(:each)
publish.map { |p| "--publish=#{p}" }
else
[]
end
end
end
end
end
end
Loading

0 comments on commit b77a046

Please sign in to comment.