bako 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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