bako 0.1.2 → 0.1.3

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.
@@ -0,0 +1 @@
1
+ 2.4.1
@@ -1,8 +1,13 @@
1
1
  sudo: false
2
2
  language: ruby
3
+ cache: bundler
3
4
  rvm:
4
5
  - 2.3.3
5
6
  before_install: gem install bundler -v 1.14.6
7
+ addons:
8
+ code_climate:
9
+ repo_token:
10
+ secure: "rzEVWgZoxVjhiqC3YwS3h2+aYLlA8FWnEuHfrvFS7kRbiOmA0h8STR7ajMS8K4nglen1UCo5uoH194UeGNeaVKUgADS/5Db4nadA9fQ8bOwpIyq9FpAA9IHqMkmQ7cHPJ6cYf4m0z25mVq4Wy7xJQQgBSSLJPGe9ZAHrE6sHd0LggsGM4St6IYqUXyf3Nshs36Mx70Kxi0YIh743frUAWDEXWv7VPtKjhLEBHN6ZB3F9W3HvwlRrpFKx5nJT6aeEdQxy+Day3cWel6a4j/VH2pm3LDGrkF9/WByu1ULCL5aUY46vKhAfVdWIucUc9ABi09+6xhI/mf8rcMUeK96qa/gCtE1f9Tn7bX8qolvJlOprJanYTiy2eOMjP0PCuWlOazW7oKoODIn74LHIZqz9Nk3zS9GmLsfc87M1BUM4ri9txoN8RlLu6mzR4Tf6D4uceDa8VlTJLCkxRyi/CDJDwIsmyWSeBHtoRn8AfeIhrZsmCCKq3giYUJawbprZB57aQ377f5r1ZasRrdUfpMXMw/6+YVqfnLzIaFfGsS2Jz5uddSIHww9fHYPEAy/0OIPG9AgmxrKFmi4DQe5O4I1wr3yuKRpMQtqJOBNS2vvryvoNmoMLLW2ThC5D1k2ig3/+FMMYNh8hsv6nYCz1o/EDnDxJ4bQAf9ulPAc/6feyqrk="
6
11
  deploy:
7
12
  provider: rubygems
8
13
  api_key:
@@ -11,3 +16,5 @@ deploy:
11
16
  on:
12
17
  tags: true
13
18
  repo: ayemos/bako
19
+ after_success:
20
+ - bundle exec codeclimate-test-reporter
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in bako.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem "codeclimate-test-reporter", "~> 1.0.0"
8
+ gem "simplecov"
9
+ end
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Bako
2
+ [![Gem Version](https://badge.fury.io/rb/bako.svg)](https://badge.fury.io/rb/bako)
3
+ [![Code Climate](https://codeclimate.com/github/ayemos/bako/badges/gpa.svg)](https://codeclimate.com/github/ayemos/bako)
4
+ [![Test Coverage](https://codeclimate.com/github/ayemos/bako/badges/coverage.svg)](https://codeclimate.com/github/ayemos/bako/coverage)
5
+ [![Issue Count](https://codeclimate.com/github/ayemos/bako/badges/issue_count.svg)](https://codeclimate.com/github/ayemos/bako)
2
6
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/bako`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
7
+ Bako is a CLI tool to manage your [AWS Batch](https://aws.amazon.com/batch/) jobs. It also has its own DSL for AWS Batch.
6
8
 
7
9
  ## Installation
8
10
 
@@ -22,7 +24,15 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ ### Prepare environment
28
+
29
+ Before start using Bako, you need to prepare basic environment on AWS Batch.
30
+ We need at least
31
+ - one Job Queue
32
+ - one Compute Environment
33
+ - and one [Amazon EC2 Container Registory](https://aws.amazon.com/jp/ecr/) (only if you use container image for batch implementation)
34
+
35
+ ## Examples
26
36
 
27
37
  ## Development
28
38
 
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.add_dependency "aws-sdk", "~> 2"
29
29
  spec.add_dependency "thor", "~> 0.19"
30
+ spec.add_dependency 'activesupport', '~> 5.0'
30
31
  end
@@ -9,6 +9,7 @@ preproc_jobs = []
9
9
  [*1..2].each do |i|
10
10
  preproc_job = job "preproc-inception-#{i}" do
11
11
  job_definition preproc_jd
12
+ job_queue 'bako-test-queue-001'
12
13
 
13
14
  param({
14
15
  foo: 'bar'
@@ -22,11 +23,12 @@ train_jd = job_definition 'train-inception' do
22
23
  command ['python', '/tensorflow/tensorflow/examples/learn/mnist.py']
23
24
  type 'container'
24
25
  memory 1024
25
- vcpus 16
26
+ vcpus 4
26
27
  image 'tensorflow/tensorflow:latest-devel'
27
28
  end
28
29
 
29
30
  job 'train-inception' do
30
31
  job_definition train_jd
31
- #depends_on preproc_jobs
32
+ job_queue 'bako-test-queue-001'
33
+ depends_on preproc_jobs
32
34
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bako/version"
2
4
  require 'bako/common_helper'
5
+ require 'logger'
3
6
 
4
7
  module Bako
5
8
  def Bako.logger
@@ -7,6 +10,8 @@ module Bako
7
10
  end
8
11
  end
9
12
 
13
+ require 'bako/error'
14
+
10
15
  require 'bako/dsl'
11
16
  require 'bako/dsl/context'
12
17
  require 'bako/dsl/context/job'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thor'
2
4
 
3
5
  module Bako
@@ -5,7 +7,9 @@ module Bako
5
7
  class_option :verbose, aliases: '-v', type: :boolean
6
8
  map '--version' => :print_version
7
9
 
8
- desc 'run', 'run job'
10
+ desc 'run JOB_FILE', 'Run batch job'
11
+ method_option :dry_run, aliases: '-n', type: :boolean,
12
+ desc: 'Run job locally without call any actual APIs'
9
13
  def runjob(path)
10
14
  require 'bako/cli/run'
11
15
  Bako::CLI::Run.new(path, options, self).run
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bako
2
4
  class CLI::Run
3
5
  include Bako::CommonHelper
@@ -9,10 +11,12 @@ module Bako
9
11
  end
10
12
 
11
13
  def run
12
- dsl.job_definitions.each do |_, jd_context|
13
- jd = Bako::Models::JobDefinition.from_context(jd_context)
14
+ dry_run = @options['dry_run']
15
+
16
+ dsl.result[:job_definitions].each do |_, jd_context|
17
+ jd = Bako::Models::JobDefinition.from_context(jd_context, dry_run: dry_run)
14
18
 
15
- if jd.remote_exists?
19
+ if !dry_run && jd.remote_exists?
16
20
  y_or_n = @thor.ask("JobDefinition #{jd.name} seems to exist on remote. would you like to update it? (y/n)")
17
21
  next unless y_or_n =~ /y/i
18
22
  end
@@ -21,14 +25,14 @@ module Bako
21
25
  end
22
26
 
23
27
  jobs_to_be_run.each do |job|
24
- Bako::Models::Job.from_context(job).start
28
+ Bako::Models::Job.from_context(job, dry_run: dry_run).start
25
29
  end
26
30
  end
27
31
 
28
32
  private
29
33
 
30
34
  def jobs_to_be_run
31
- dsl.jobs.values - dsl.jobs.values.map{|j| j.depends_on_b}.flatten
35
+ dsl.result[:jobs].values - dsl.result[:jobs].values.map{|j| j.result[:depends_on]}.flatten
32
36
  end
33
37
 
34
38
  def dsl
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aws-sdk'
2
4
 
3
5
  module Bako
@@ -5,12 +7,11 @@ module Bako
5
7
  DEFAULTS = {
6
8
  vcpus: 1,
7
9
  memory: 128
8
- }
10
+ }.freeze
11
+
9
12
  def batch_client
10
13
  @batch_client ||= Aws::Batch::Client.new
11
14
  end
12
- def remote_resource(arn)
13
- end
14
15
 
15
16
  def remote_job_definition(name_or_arn, status=nil)
16
17
  remote_job_definitions(status).find{|jd|
@@ -24,12 +25,14 @@ module Bako
24
25
  }).job_definitions.map do |jd|
25
26
  Bako::Models::JobDefinition.new(
26
27
  jd.job_definition_name,
27
- jd.container_properties&.command,
28
- jd.container_properties&.image,
29
- jd.container_properties&.job_role_arn,
30
- jd.type,
31
- jd.container_properties&.memory,
32
- jd.container_properties&.vcpus
28
+ {
29
+ command: jd.container_properties&.command,
30
+ image: jd.container_properties&.image,
31
+ role_arn: jd.container_properties&.job_role_arn,
32
+ type: jd.type,
33
+ memory: jd.container_properties&.memory,
34
+ vcpus: jd.container_properties&.vcpus,
35
+ }
33
36
  )
34
37
  end
35
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Bako::DSL
2
4
  class << self
3
5
  def parse(dsl)
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ require 'active_support/core_ext/string/inflections'
4
+
1
5
  class Bako::DSL::Context
2
- attr_reader :jobs, :job_definitions
6
+ attr_reader :result
3
7
 
4
8
  def self.eval(dsl)
5
9
  new do
@@ -8,31 +12,34 @@ class Bako::DSL::Context
8
12
  end
9
13
 
10
14
  def initialize(&block)
11
- @jobs = {}
12
- @job_definitions = {}
15
+
16
+ @result = {
17
+ jobs: {},
18
+ job_definitions: {},
19
+ }
13
20
 
14
21
  instance_eval(&block)
15
22
  end
16
23
 
17
24
  private
18
25
 
19
- def job(name, &block)
20
- name = name.to_s
26
+ %i[
27
+ job
28
+ job_definition
29
+ ].each do |context|
30
+ define_method(context) do |name, &block|
31
+ name = name.to_s
32
+ result_key = context.to_s.pluralize.to_sym
21
33
 
22
- if @jobs[name]
23
- raise "Job `#{name}` is already defined"
24
- end
34
+ if result[result_key][name]
35
+ raise "#{context.to_s.capitalize} `#{name}` is already defined"
36
+ end
25
37
 
26
- @jobs[name] = Bako::DSL::Context::Job.new(name, &block)
27
- end
28
-
29
- def job_definition(name, &block)
30
- name = name.to_s
31
-
32
- if @job_definitions[name]
33
- raise "JobDefinition `#{name}` is already defined"
38
+ @result[result_key][name] = load_context(context).new(name, &block)
34
39
  end
40
+ end
35
41
 
36
- @job_definitions[name] = Bako::DSL::Context::JobDefinition.new(name, &block)
42
+ def load_context(context)
43
+ Object.const_get("Bako::DSL::Context::#{context.to_s.camelize}")
37
44
  end
38
45
  end
@@ -1,21 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bako
2
4
  class DSL::Context::Job
3
- attr_reader :name, :param_b, :job_definition_b, :depends_on_b, :memory_b, :vcpus_b, :job_queue_b
5
+ attr_reader :name, :result
4
6
 
5
7
  def initialize(name, &block)
6
8
  @name = name
9
+ @result = {}
7
10
 
8
11
  instance_eval(&block)
12
+ validate!
9
13
  end
10
14
 
11
15
  private
12
16
 
17
+ %i[
18
+ param
19
+ memory
20
+ vcpus
21
+ job_queue
22
+ ].each do |attr|
23
+ define_method(attr) do |v|
24
+ result[attr] = v
25
+ end
26
+ end
27
+
28
+ def validate!
29
+ raise InvalidArgumentError.new('job_queue must be set') unless result[:job_queue]
30
+ raise InvalidArgumentError.new('job_definition must be set') unless result[:job_definition]
31
+ end
32
+
13
33
  def job_definition(jd)
14
34
  if jd.nil?
15
35
  raise InvalidArgumentError.new('JobDefinition must be set')
16
36
  end
17
37
 
18
- @job_definition_b =
38
+ result[:job_definition] =
19
39
  if jd.is_a?(Bako::DSL::Context::JobDefinition)
20
40
  jd
21
41
  elsif jd.is_a?(String)
@@ -26,7 +46,7 @@ module Bako
26
46
  end
27
47
 
28
48
  def depends_on(jobs)
29
- @depends_on_b = jobs&.map do |job|
49
+ result[:depends_on] = jobs&.map do |job|
30
50
  if job.is_a?(Bako::DSL::Context::Job)
31
51
  job
32
52
  elsif job.is_a?(String)
@@ -37,20 +57,12 @@ module Bako
37
57
  end
38
58
  end
39
59
 
40
- def param(h)
41
- @param_b = h
42
- end
43
-
44
- def memory(memory_b)
45
- @memory_b = memory_b
46
- end
47
-
48
- def vcpus(vcpus_b)
49
- @vcpus_b = vcpus_b
50
- end
51
-
52
- def job_queue(job_queue_b)
53
- @job_queue_b = job_queue_b
60
+ def command(v)
61
+ result[:command] = if v.is_a?(Array)
62
+ v.map(&:to_s)
63
+ else
64
+ v.split.map(&:to_s)
65
+ end
54
66
  end
55
67
  end
56
68
  end
@@ -1,39 +1,34 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Bako
3
4
  class DSL::Context::JobDefinition
4
- attr_reader :name, :type_b, :image_b, :command_b, :role_arn_b, :memory_b, :vcpus_b
5
+ attr_reader :name, :result
5
6
 
6
7
  def initialize(name, &block)
7
8
  @name = name
8
- @type_b = 'container'
9
+ @result = {}
9
10
 
10
11
  instance_eval(&block)
12
+ validate!
11
13
  end
12
14
 
13
15
  private
14
16
 
15
- def type(job_type)
16
- @type_b = job_type
17
+ def validate!
18
+ raise InvalidArgumentError.new('type must be set') unless result[:type]
17
19
  end
18
20
 
19
- def image(image_b)
20
- @image_b = image_b
21
- end
22
-
23
- def command(command_b)
24
- @command_b = command_b
25
- end
26
-
27
- def role_arn(role_arn_b)
28
- @role_arn_b = role_arn_b
29
- end
30
-
31
- def memory(memory_b)
32
- @memory_b = memory_b
33
- end
34
-
35
- def vcpus(vcpus_b)
36
- @vcpus_b = vcpus_b
21
+ %i[
22
+ type
23
+ image
24
+ command
25
+ role_arn
26
+ memory
27
+ vcpus
28
+ ].each do |attr|
29
+ define_method(attr) do |v|
30
+ result[attr] = v
31
+ end
37
32
  end
38
33
  end
39
34
  end
@@ -1,4 +1,6 @@
1
- module Hako
1
+ # frozen_string_literal: true
2
+
3
+ module Bako
2
4
  class InvalidArgumentError < StandardError
3
5
  end
4
6
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
  require 'pathname'
3
5
 
@@ -6,50 +8,62 @@ module Bako
6
8
  class Job
7
9
  include Bako::CommonHelper
8
10
 
9
- attr_reader :name, :job_definition, :depends_on, :param, :id, :memory, :vcpus, :job_queue
11
+ attr_reader :name, :job_definition, :depends_on, :param, :id, :memory, :vcpus, :job_queue, :command
10
12
 
11
- def self.from_context(context)
13
+ def self.from_context(context, dry_run: false)
12
14
  new(
13
15
  context.name,
14
- context.job_definition_b,
15
- context.depends_on_b&.map{|job_context|
16
- Bako::Models::Job.from_context(job_context)
17
- },
18
- context.param_b,
19
- context.memory_b,
20
- context.vcpus_b,
21
- context.job_queue_b
16
+ context.result,
17
+ dry_run: dry_run
22
18
  )
23
19
  end
24
20
 
25
- def initialize(name, job_definition, depends_on, param, memory, vcpus, job_queue)
21
+ def initialize(name, result, dry_run: false)
26
22
  @name = name
27
- @job_definition = job_definition
28
- @depends_on = depends_on
29
- @param = param
30
- @memory = memory
31
- @vcpus = vcpus
32
- @job_queue = job_queue
23
+ @job_definition = result[:job_definition]
24
+ @depends_on = result[:depends_on]&.map{|job_context|
25
+ Bako::Models::Job.from_context(job_context)
26
+ }
27
+ @param = result[:param]
28
+ @memory = result[:memory]
29
+ @vcpus = result[:vcpus]
30
+ @job_queue = result[:job_queue]
31
+ @command = result[:command]
32
+ @dry_run = dry_run
33
33
  end
34
34
 
35
35
  def start
36
- # start 'depends_on' jobs first
37
- d_ids = []
36
+ _start(dry_run: @dry_run)
37
+ end
38
38
 
39
- @depends_on&.each do |job|
40
- d_ids << job.start.job_id
41
- end
39
+ def _start(dry_run: false)
40
+ # start 'depends_on' jobs first
41
+ @depends_on&.each{|j| j._start(dry_run: dry_run)}
42
42
 
43
- # start job
44
- resp = batch_client.submit_job({
43
+ Bako.logger.info("Submitting job #{@name}:")
44
+ job_arg = {
45
45
  job_definition: @job_definition.name,
46
46
  job_name: @name,
47
47
  job_queue: @job_queue,
48
48
  depends_on: @depends_on&.map{|j| [[:job_id, j.id]].to_h},
49
- parameters: @param
50
- })
49
+ parameters: @param,
50
+ container_overrides: {
51
+ command: @command,
52
+ vcpus: @vcpus,
53
+ memory: @memory
54
+ }
55
+ }
56
+
57
+ Bako.logger.info("\n#{job_arg.to_yaml}\n")
58
+
59
+ # start job
60
+ if dry_run
61
+ @id = '(dry_run)'
62
+ else
63
+ resp = batch_client.submit_job(job_arg)
64
+ @id = resp.job_id
65
+ end
51
66
 
52
- @id = resp.job_id
53
67
  Bako.logger.info("Submitted job #{@name} (id: #{@id})")
54
68
 
55
69
  resp