133 lines
4.8 KiB
Go
133 lines
4.8 KiB
Go
package app
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"coopcloud.tech/abra/pkg/i18n"
|
|
"coopcloud.tech/abra/pkg/log"
|
|
composetypes "github.com/docker/cli/cli/compose/types"
|
|
)
|
|
|
|
// setLabel writes a label to both the service object and the task template of
|
|
// every service of the stack.
|
|
//
|
|
// The task template labels end up on the containers themselves and are therefore
|
|
// visible to container-level tooling (cAdvisor, log shippers, docker events),
|
|
// which cannot read service labels at all.
|
|
//
|
|
// The service object is written as well because it is free: changing
|
|
// Spec.Labels updates the service without recreating any task and because
|
|
// `docker service ls --filter label=...` matches service labels only.
|
|
func setLabel(compose *composetypes.Config, key string, value string) {
|
|
for i := range compose.Services {
|
|
if compose.Services[i].Deploy.Labels == nil {
|
|
compose.Services[i].Deploy.Labels = composetypes.Labels{}
|
|
}
|
|
|
|
if compose.Services[i].Labels == nil {
|
|
compose.Services[i].Labels = composetypes.Labels{}
|
|
}
|
|
|
|
compose.Services[i].Deploy.Labels[key] = value
|
|
compose.Services[i].Labels[key] = value
|
|
}
|
|
}
|
|
|
|
// setAppLabel writes a label to the service object of the app service only.
|
|
func setAppLabel(compose *composetypes.Config, key string, value string) {
|
|
for i := range compose.Services {
|
|
if compose.Services[i].Name != "app" {
|
|
continue
|
|
}
|
|
|
|
if compose.Services[i].Deploy.Labels == nil {
|
|
compose.Services[i].Deploy.Labels = composetypes.Labels{}
|
|
}
|
|
|
|
compose.Services[i].Deploy.Labels[key] = value
|
|
}
|
|
}
|
|
|
|
// SetRecipeLabel adds two distinct labels to signal which recipe is connected
|
|
// to the deployed app:
|
|
// - 'coop-cloud.${STACK_NAME}.recipe=${RECIPE}' on the app service object,
|
|
// unchanged, as read back by GetLabel
|
|
// - 'coop-cloud.recipe=${RECIPE}' on every service of the stack, so that
|
|
// tooling can attribute any container to a recipe without knowing the
|
|
// stack name up front
|
|
func SetRecipeLabel(compose *composetypes.Config, stackName string, recipe string) {
|
|
log.Debug(i18n.G("set recipe labels 'coop-cloud.%s.recipe' and 'coop-cloud.recipe' to %s for %s", stackName, recipe, stackName))
|
|
|
|
setAppLabel(compose, fmt.Sprintf("coop-cloud.%s.recipe", stackName), recipe)
|
|
setLabel(compose, "coop-cloud.recipe", recipe)
|
|
}
|
|
|
|
// SetChaosLabel adds the label 'coop-cloud.${STACK_NAME}.chaos=true/false' to the app container
|
|
// to signal if the app is deployed in chaos mode
|
|
func SetChaosLabel(compose *composetypes.Config, stackName string, chaos bool) {
|
|
for _, service := range compose.Services {
|
|
if service.Name == "app" {
|
|
log.Debug(i18n.G("set label 'coop-cloud.%s.chaos' to %v for %s", stackName, chaos, stackName))
|
|
labelKey := fmt.Sprintf("coop-cloud.%s.chaos", stackName)
|
|
service.Deploy.Labels[labelKey] = strconv.FormatBool(chaos)
|
|
}
|
|
}
|
|
}
|
|
|
|
// SetChaosVersionLabel adds the label 'coop-cloud.${STACK_NAME}.chaos-version=$(GIT_COMMIT)' to the app container
|
|
func SetChaosVersionLabel(compose *composetypes.Config, stackName string, chaosVersion string) {
|
|
for _, service := range compose.Services {
|
|
if service.Name == "app" {
|
|
log.Debug(i18n.G("set label 'coop-cloud.%s.chaos-version' to %v for %s", stackName, chaosVersion, stackName))
|
|
labelKey := fmt.Sprintf("coop-cloud.%s.chaos-version", stackName)
|
|
service.Deploy.Labels[labelKey] = chaosVersion
|
|
}
|
|
}
|
|
}
|
|
|
|
func SetVersionLabel(compose *composetypes.Config, stackName string, version string) {
|
|
for _, service := range compose.Services {
|
|
if service.Name == "app" {
|
|
log.Debug(i18n.G("set label 'coop-cloud.%s.version' to %v for %s", stackName, version, stackName))
|
|
labelKey := fmt.Sprintf("coop-cloud.%s.version", stackName)
|
|
service.Deploy.Labels[labelKey] = version
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetLabel reads docker labels in the format of "coop-cloud.${STACK_NAME}.${LABEL}" from the local compose files
|
|
func GetLabel(compose *composetypes.Config, stackName string, label string) string {
|
|
for _, service := range compose.Services {
|
|
if service.Name == "app" {
|
|
labelKey := fmt.Sprintf("coop-cloud.%s.%s", stackName, label)
|
|
log.Debug(i18n.G("get label '%s'", labelKey))
|
|
if labelValue, ok := service.Deploy.Labels[labelKey]; ok {
|
|
return labelValue
|
|
}
|
|
}
|
|
}
|
|
log.Debug(i18n.G("no %s label found for %s", label, stackName))
|
|
return ""
|
|
}
|
|
|
|
// GetTimeoutFromLabel reads the timeout value from docker label
|
|
// `coop-cloud.${STACK_NAME}.timeout=...` if present. A value is present if the
|
|
// operator uses a `TIMEOUT=...` in their app env.
|
|
func GetTimeoutFromLabel(compose *composetypes.Config, stackName string) (int, error) {
|
|
var timeout int
|
|
|
|
if timeoutLabel := GetLabel(compose, stackName, "timeout"); timeoutLabel != "" {
|
|
log.Debug(i18n.G("timeout label: %s", timeoutLabel))
|
|
|
|
var err error
|
|
timeout, err = strconv.Atoi(timeoutLabel)
|
|
if err != nil {
|
|
return timeout, errors.New(i18n.G("unable to convert timeout label %s to int: %s", timeoutLabel, err))
|
|
}
|
|
}
|
|
|
|
return timeout, nil
|
|
}
|