Class: Dctl::Main

Inherits:
Object
  • Object
show all
Defined in:
lib/dctl/main.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env: "dev", config: nil) ⇒ Main

Returns a new instance of Main.



5
6
7
8
# File 'lib/dctl/main.rb', line 5

def initialize(env: "dev", config: nil)
  @env = env
  load_config!(config)
end

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



3
4
5
# File 'lib/dctl/main.rb', line 3

def env
  @env
end

#settingsObject (readonly)

Returns the value of attribute settings.



3
4
5
# File 'lib/dctl/main.rb', line 3

def settings
  @settings
end

Instance Method Details

#bump(image) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/dctl/main.rb', line 54

def bump(image)
  check_image(image)

  parsed  = parsed_compose_file
  service = parsed.dig "services", image
  old_tag = service["image"]
  puts "Found existing image #{old_tag}"

  version = versions[image].to_i
  new_tag = image_tag image, version: version + 1
  puts "New tag will be #{new_tag}"

  service["image"] = new_tag

  print "Updating..."
  File.write(compose_file_path, parsed.to_yaml)
  puts "done"

  # Cache bust
  @parsed_compose_file = nil
  @versions = nil

  puts Rainbow("#{image} is now at version #{version + 1}").fg :green
end

#check_image(image) ⇒ Object

Confirms that there is an entry for the given image in the compose file for this environment, and that the image tag within is formatted as we expect it to be.

Prints a warning if the tag has the wrong name, but errors out if the service tag is not present

Expected names look like org/project-env-image:version



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/dctl/main.rb', line 102

def check_image(image)
  tag = image_tag(image)

  # Check that a service exists for the image
  service = parsed_compose_file.dig "services", image
  unless service
    error = "The service \"#{image}\" is not present in the compose " \
      "file for this environment. Please add a service entry for " \
      "#{image} to #{compose_file_path}\n"
    puts Rainbow(error).fg :red

    puts "      It might look something like this:\n\n      version: '3'\n      services:\n        \#{image}:\n          image: \#{image_tag(image)}\n    EOL\n    exit 1\n  end\n\n  # Check that the image has the correct tag\n  expected_tag = image_tag(image)\n  actual_tag = service[\"image\"]\n  if actual_tag != expected_tag\n    warning = \"Expected the tag for the image \\\"\#{image}\\\" to be \" \\\n      \"\\\"\#{expected_tag}\\\", but it was \\\"\#{actual_tag}\\\". While not \" \\\n      \"critical, this can cause issues with some commands.\"\n    puts Rainbow(warning).fg :orange\n  end\nend\n"

#check_settings!Object

Ensure the current project’s .dctl.yml contains all the requisite keys.



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/dctl/main.rb', line 166

def check_settings!
  required_keys = %w(
    org
    project
  )

  required_keys.each do |key|
    unless Settings.send key
      error = "Config is missing required key '#{key}'. Please add it " \
        "to #{config_path} and try again."
      error += "\n\nFor more info, see https://github.com/jutonz/dctl_rb#required-keys"
      puts Rainbow(error).red
      exit 1
    end
  end
end

#compose_file_pathObject



193
194
195
196
197
198
199
200
201
202
203
# File 'lib/dctl/main.rb', line 193

def compose_file_path
  path = File.expand_path "docker/#{env}/docker-compose.yml"

  unless File.exist? path
    err = "There is no docker compose file for env #{env} (I expected to find it at #{path})"
    puts Rainbow(err).red
    exit 1
  end

  path
end

#config_pathObject

Returns the path to the .dctl.yml file for the current project



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/dctl/main.rb', line 81

def config_path
  path = File.expand_path ".dctl.yml", Dir.pwd

  unless File.exist? path
    error = "Could not find config file at #{path}"
    puts Rainbow(error).red
    exit 1
  end

  path
end

#current_version_for_image(image) ⇒ Object



28
29
30
# File 'lib/dctl/main.rb', line 28

def current_version_for_image(image)
  versions[image]
end

#define_custom_commands(klass) ⇒ Object

If there are user defined commands in .dctl.yml, dynamically add them to the passed thor CLI so they may be executed.



155
156
157
158
159
160
161
162
# File 'lib/dctl/main.rb', line 155

def define_custom_commands(klass)
  Array(settings.custom_commands).each do |command, args|
    klass.send(:desc, command, "[Custom Command] #{command}")
    klass.send(:define_method, command, -> do
      Array(args).each { |a| stream_output(a) }
    end)
  end
end

#expand_images(*images) ⇒ Object



45
46
47
48
49
50
51
52
# File 'lib/dctl/main.rb', line 45

def expand_images(*images)
  images = versions.keys if images.empty?
  images = Array(images)

  images.each { |image| check_image(image) }

  images
end

#image_dir(image) ⇒ Object

Returns the path to the given image’s data directory (which includes at minimum the Dockerfile, plus any other relevant files the user may have placed there).



36
37
38
39
# File 'lib/dctl/main.rb', line 36

def image_dir(image)
  relative = File.join "docker", env, image
  File.expand_path relative, Dir.pwd
end

#image_dockerfile(image) ⇒ Object



41
42
43
# File 'lib/dctl/main.rb', line 41

def image_dockerfile(image)
  File.expand_path "Dockerfile", image_dir(image)
end

#image_tag(image, version: current_version_for_image(image)) ⇒ Object

Generate the full tag for the given image, concatenating the org, project, env, image name, and version.

Pass ‘version: nil` to exclude the version portion.

Examples:

image_tag("app") # => jutonz/dctl-dev-app:1


18
19
20
21
22
23
24
25
26
# File 'lib/dctl/main.rb', line 18

def image_tag(image, version: current_version_for_image(image))
  org       = settings.org
  project   = settings.project

  tag = "#{org}/#{project}-#{env}-#{image}"
  tag += ":#{version}" if !version.nil?

  tag
end

#load_config!(custom_config_path = nil) ⇒ Object

Load the current project’s config file, complaining if it does not exist or is malformed.



186
187
188
189
190
191
# File 'lib/dctl/main.rb', line 186

def load_config!(custom_config_path = nil)
  Config.load_and_set_settings(custom_config_path || config_path)
  check_settings!

  @settings = Settings
end

#parsed_compose_fileObject



148
149
150
# File 'lib/dctl/main.rb', line 148

def parsed_compose_file
  @parsed_compose_file ||= YAML.load_file compose_file_path
end

#versionsObject



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/dctl/main.rb', line 135

def versions
  @versions ||= begin
    images = parsed_compose_file["services"].keys
    version_map = {}

    images.each do |image|
      version_map[image] = parsed_compose_file["services"][image]["image"].split(":").last
    end

    version_map
  end
end