class Siren::Compose::Service

Public Class Methods

new(compose, data = {}) click to toggle source
# File lib/siren/compose/service.rb, line 12
def initialize (compose, data = {})
  self.compose = compose
  data.each do |key, value|
    self.__send__("#{key}=", value)
  end
  self.deploy = deploy ? Deploy.new(deploy) : Deploy.new
  self.ports ||= []
  self.volumes ||= []
  self.name = namify(self.name)
end

Public Instance Methods

command=(value) click to toggle source
Calls superclass method
# File lib/siren/compose/service.rb, line 59
def command= (value)
  value = ["sh", "-c", value] if value.is_a?(String)
  super(value)
  # if configs
end
emit_auth_middleware(stack) click to toggle source
# File lib/siren/compose/service.rb, line 168
def emit_auth_middleware (stack)
  return if @did_emit_auth_middleware
  did_emit_auth_middleware = true
  stack << {
    kind: "Middleware",
    apiVersion: "traefik.containo.us/v1alpha1",
    metadata: {
      namespace: compose.name,
      name: "altaire-auth",
    },
    spec: {
      forwardAuth: {
        address: "http://auth-server.default.svc.cluster.local/auth",
      },
    },
  }
end
emit_certificates(stack) click to toggle source
# File lib/siren/compose/service.rb, line 261
def emit_certificates (stack)
  compose.xdomains.each do |xd|
    stack << {
      kind: "Certificate",
      apiVersion: "cert-manager.io/v1alpha2",
      metadata: {
        namespace: compose.name,
        name: namify(xd["name"]),
      },
      spec: {
        commonName: xd["name"],
        dnsNames: [xd["name"]],
        duration: "2160h0m0s",
        renewBefore: "360h0m0s",
        issuerRef: {
          kind: "ClusterIssuer",
          name: "letsencrypt-production",
        },
        secretName: namify(xd["name"]),
      },
    }
  end
end
emit_deployment(stack) click to toggle source
# File lib/siren/compose/service.rb, line 90
def emit_deployment (stack)
  stack << {
    kind: "Deployment",
    apiVersion: "apps/v1",
    metadata: {
      namespace: compose.name,
      name: name,
    },
    spec: {
      replicas: deploy.replicas,
      selector: {
        matchLabels: {
          stack: compose.name,
          service: name,
        },
      },
      template: {
        metadata: {
          labels: {
            stack: compose.name,
            service: name,
          },
        },
        spec: {
          containers: [
            command: command,
            env: resolve_environment(environment),
            image: image,
            name: name,
            volumeMounts: volumes.map do |volume|
              name, path = volume.split(":", 2)
              {
                mountPath: path,
                name: name,
              }
            end
          ],
          hostname: hostname,
          imagePullSecrets: [{name: "image-pull-secrets"}],
          restartPolicy: { "none" => "Never", "on-failure" => "OnFailure" }[deploy.restart_policy&.condition],
          volumes: volumes.map do |volume|
            name = volume.split(":")[0]
            { name: name, persistentVolumeClaim: { claimName: name } }
          end
        },
      },
    },
  }
end
emit_domains(stack) click to toggle source
# File lib/siren/compose/service.rb, line 244
def emit_domains (stack)
  compose.xdomains.each do |xd|
    stack << {
      kind: "Domain",
      apiVersion: "altaire.com/v1alpha1",
      metadata: {
        namespace: compose.name,
        name: namify(xd["name"]),
      },
      spec: {
        domain: xd["name"],
        service: "traefik",
      },
    }
  end
end
emit_ingress_routes(stack) click to toggle source
# File lib/siren/compose/service.rb, line 206
def emit_ingress_routes (stack)
  compose.xdomains.each do |xd|
    next unless xd["service"] == name
    emit_auth_middleware stack if xd["auth"]
    stack << {
      kind: "IngressRoute",
      apiVersion: "traefik.containo.us/v1alpha1",
      metadata: {
        namespace: compose.name,
        name: namify(xd["name"], xd["path"]),
      },
      spec: {
        tls: {
          secretName: namify(xd["name"]),
        },
        routes: [{
          kind: "Rule",
          match: nil,
          middlewares: [
            xd["auth"] ? { name: "altaire-auth" } : nil,
            xd["path"] ? { name: namify(xd["name"], xd["path"], "strip-prefix") } : nil,
          ],
          services: [{
            name: name,
            port: 80
          }],
        }.merge(
          if xd["path"]
            {match: "Host(`#{xd["name"]}`) && PathPrefix(`#{xd["path"]}`)"}
          else
            {match: "Host(`#{xd["name"]}`)"}
          end
        )],
      },
    }
  end
end
emit_middlewares(stack) click to toggle source
# File lib/siren/compose/service.rb, line 186
def emit_middlewares (stack)
  compose.xdomains.each do |xd|
    next unless xd["service"] == name
    next if xd["path"] == nil
    stack << {
      kind: "Middleware",
      apiVersion: "traefik.containo.us/v1alpha1",
      metadata: {
        namespace: compose.name,
        name: namify(xd["name"], xd["path"], "strip-prefix"),
      },
      spec: {
        stripPrefix: {
          prefixes: ["/#{xd["path"]}"],
        },
      },
    }
  end
end
emit_services(stack) click to toggle source
# File lib/siren/compose/service.rb, line 140
def emit_services (stack)
  ports.group_by{|port|port["mode"]}.each do |mode, ports|
    stack << {
      kind: "Service",
      apiVersion: "v1",
      metadata: {
        namespace: compose.name,
        name: mode == "ingress" ? "#{name}-nodeport" : name,
      },
      spec: {
        ports: ports.map do |port|
          {
            name: ports.length == 1 ? nil : port["published"],
            port: port["published"].to_i,
            targetPort: port["target"] != port["published"] ? port["target"].to_i : nil,
            protocol: port["protocol"] == "udp" ? "UDP" : nil
          }
        end,
        selector: {
          stack: compose.name,
          service: name,
        },
        type: mode == "ingress" ? "NodePort" : nil,
      }
    }
  end
end
emit_volumes(stack) click to toggle source
# File lib/siren/compose/service.rb, line 285
def emit_volumes (stack)
  volumes.each do |volume|
    name, path = volume.split(":", 2)
    stack << {
      kind: "PersistentVolumeClaim",
      apiVersion: "v1",
      metadata: {
        namespace: compose.name,
        name: name,
      },
      spec: {
        accessModes: ["ReadWriteOnce"],
        resources: {
          requests: {
            storage: "1Gi",
          },
        },
        storageClassName: "do-block-storage",
      },
    }
  end
end
namify(*items) click to toggle source
# File lib/siren/compose/service.rb, line 86
def namify (*items)
  items.flatten.compact.join("-").downcase.gsub(/[^a-z\d]/, '-').gsub(/-+/, "-")
end
ports=(ports) click to toggle source
Calls superclass method
# File lib/siren/compose/service.rb, line 23
def ports= (ports)
  ports = ports.map do |port|
    if port.is_a?(String)
      target, published, protocol = port.match(/^(\d+)?(?::(\d+))?(\/.+)?$/).to_a[1..-1]
    else
      target, published, protocol, mode = port.values_at("target", "published", "protocol", "mode")
    end
    protocol ||= "tcp"
    {
      "target" => target,
      "published" => published,
      "protocol" => protocol,
      "mode" => mode,
    }
  end
  compose.xdomains.each do |xd|
    next unless xd["service"] == name
    ports.unshift({
      "protocol" => "tcp",
      "target" => xd["port"],
      "published" => "80",
    })
  end
  compose.xports.each do |xp|
    next unless xp["service"] == name
    ports.unshift({
      "protocol" => xp["protocol"],
      "target" => xp["inside"],
      "published" => xp["outside"],
      "mode" => "ingress",
    })
  end
  ports.uniq!{|p|p.values_at("published", "protocol")}
  super(ports)
end
resolve_environment(env) click to toggle source
# File lib/siren/compose/service.rb, line 65
def resolve_environment (env)
  if env.is_a?(Array)
    env = env.map do |line|
      key, value = line.split("=", 2)
      [key, value || "$#{key}"]
    end.to_h
  end
  env ||= {}
  env.transform_values! do |value|
    value.gsub(/\$[\dA-Z_a-z]+/) do |name|
      compose.xenv[name[1..-1]]
    end
  end
  env.map do |name, value|
    {
      name: name,
      value: value,
    }
  end
end
to_stack(stack) click to toggle source
# File lib/siren/compose/service.rb, line 308
def to_stack (stack)
  emit_deployment stack
  emit_services stack
  emit_middlewares stack
  emit_ingress_routes stack
  emit_domains stack
  emit_certificates stack
  emit_volumes stack
end