Packages

helijia Skylab deployment package

Current section

Files

Jump to
helijia_skylab lib skylab image.ex
Raw

lib/skylab/image.ex

defmodule Skylab.Image do
require Logger
import System, only: [cmd: 3, cmd: 2]
@moduledoc """
镜像相关的功能
"""
def gen(opts) do
with {:build, :ok} <- {:build, gen_image(opts)},
{:push, :ok} <- {:push, push_image(opts)} do
:ok
end
end
defp gen_image(opts) do
opts
|> revision()
|> build_image(opts)
end
def revision(kw) when is_list(kw) do
revision(kw[:rev])
end
def revision(nil) do
{rev, 0} = cmd("git", ~w[rev-parse --verify HEAD --short])
rev |> String.trim_trailing()
end
def revision(rev), do: rev
defp build_image(rev, opts) do
dockerfile = Keyword.get(opts, :dockerfile, "Dockerfile")
app_ver = app_version(opts)
name = image_name(opts)
args = [
"--file",
dockerfile,
"--build-arg",
"app_name=#{project_config(:app)}",
"--build-arg",
"app_vsn=#{app_ver}",
"--build-arg",
"app_revision=#{rev}",
"--build-arg",
"node_env=#{Mix.env()}",
"--tag",
name
]
{_, 0} =
cmd(
"docker",
~w[build] ++ args ++ ~w[.],
into: IO.stream(:stdio, :line)
)
:ok
end
defp project_config(key) do
Mix.Project.config()
|> Keyword.get(key)
end
# 生成镜像名称, 格式为:
#
# #{app}:#{version}-#{rev}
#
# ## Example:
#
# iex> Skylab.Image.image_name([app: "foo", k8s_namespace: "pro", version: "0.0.1", rev: "sha1"])
# foo:0.0.1-sha1-pro
def image_name(opts) do
app =
opts
|> Keyword.get(:app, project_config(:app))
"#{app}:#{app_version(opts)}-#{revision(opts)}-#{app_k8s_namespace(opts)}"
end
def app_version(opts) do
opts[:version] || project_config(:version)
end
defp app_k8s_namespace(opts), do: opts[:k8s_namespace]
defp push_image(opts) do
case opts[:push] do
true ->
do_push_image(opts)
_ ->
:ok
end
end
defp do_push_image(opts) do
image = image_name(opts)
full = full_image_name(opts)
docker_tag(image, full)
docker_login(opts)
docker_push(full)
:ok
end
# 完整的镜像名称,格式为:
#
# #{registry}/#{namespace}/#{app}:#{version}-#{revision}
#
# - registry: 镜像仓库地址
# - namespace: 镜像的命名空间
# - app: 应用名称
# - version: 应用版本号,如 0.1.0
# - revision: 应用 hash 版本
#
# ## Example
#
# iex> Skylab.Image.full_image_name([
# ...> app: "my-app",
# ...> version: "0.1.0",
# ...> rev: "sha-2",
# ...> regisitry: "reg.example.com",
# ...> namespace: "ns"
# ...> ])
# "reg.example.com/ns/my-app:0.1.0-sha2"
def full_image_name(opts) do
opts
|> image_name()
|> with_registry(opts)
end
def full_image_name_to_pull(opts) do
opts
|> image_name()
|> with_registry(opts, :pull)
end
defp with_registry(image, opts) do
[opts[:registry], opts[:namespace], image]
|> Enum.reject(&is_nil/1)
|> Path.join()
end
defp with_registry(image, opts, :pull) do
case opts[:pull_registry] do
nil ->
with_registry(image, opts)
reg ->
with_registry(image, Keyword.put(opts, :registry, reg))
end
end
defp docker_tag(image, full) do
{_, 0} =
System.cmd(
"docker",
["tag", image, full],
into: IO.stream(:stdio, :line)
)
Logger.info(fn -> "push image: #{full}" end)
end
defp docker_login(opts) do
case registry_user(opts) do
nil ->
:ok
_ ->
do_docker_login(opts)
end
end
def registry_user(opts) do
opts[:registry_user]
end
def registry_password(opts) do
case opts[:registry_password] do
nil ->
nil
p when is_binary(p) ->
p
|> Base.decode64!()
|> String.trim()
end
end
defp do_docker_login(opts) do
{_, 0} =
System.cmd(
"docker",
[
"login",
"--username",
registry_user(opts),
"--password",
registry_password(opts),
opts[:registry]
],
into: IO.stream(:stdio, :line)
)
:ok
end
defp docker_push(full) do
{_, 0} =
System.cmd(
"docker",
["push", full],
into: IO.stream(:stdio, :line)
)
end
end