ecs-agent/manageddaemon/managed_daemon.go (256 lines of code) (raw):
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
package manageddaemon
import (
"fmt"
"path/filepath"
"time"
"github.com/aws/amazon-ecs-agent/ecs-agent/acs/model/ecsacs"
dockercontainer "github.com/docker/docker/api/types/container"
)
const (
imageFileName = "ebs-csi-driver.tar"
)
type ManagedDaemon struct {
imageName string
imageTag string
healthCheckTest []string
healthCheckInterval time.Duration
healthCheckTimeout time.Duration
healthCheckRetries int
// Daemons require an agent <-> daemon mount
// identified by the volume name `agentCommunicationMount`
// the SourceVolumeHostPath will always be overridden to
// /var/run/ecs/<md.imageName>
agentCommunicationMount *MountPoint
// Daemons require an application log mount
// identified by the volume name `applicationLogMount`
// the SourceVolumeHostPath will always be overridden to
// /var/log/ecs/<md.imageName>
applicationLogMount *MountPoint
mountPoints []*MountPoint
environment map[string]string
loadedDaemonImageRef string
command []string
linuxParameters *ecsacs.LinuxParameters
privileged bool
containerId string
containerCGroup string
networkNameSpace string
}
// A valid managed daemon will require
// healthcheck and mount points to be added
func NewManagedDaemon(
imageName string,
imageTag string,
) *ManagedDaemon {
if imageTag == "" {
imageTag = imageTagDefault
}
// health check retries 0 is valid
newManagedDaemon := &ManagedDaemon{
imageName: imageName,
imageTag: imageTag,
healthCheckRetries: 0,
}
return newManagedDaemon
}
var ImportAll = defaultImportAll
func (md *ManagedDaemon) GetLinuxParameters() *ecsacs.LinuxParameters {
return md.linuxParameters
}
func (md *ManagedDaemon) GetPrivileged() bool {
return md.privileged
}
func (md *ManagedDaemon) GetImageName() string {
return md.imageName
}
func (md *ManagedDaemon) GetImageTag() string {
return md.imageTag
}
func (md *ManagedDaemon) GetImageRef() string {
return (fmt.Sprintf("%s:%s", md.imageName, md.imageTag))
}
func (md *ManagedDaemon) GetImageTarPath() string {
return (fmt.Sprintf("%s/%s/%s.tar", imageTarPath, md.imageName, md.imageName))
}
func (md *ManagedDaemon) GetAgentCommunicationMount() *MountPoint {
return md.agentCommunicationMount
}
func (md *ManagedDaemon) GetApplicationLogMount() *MountPoint {
return md.applicationLogMount
}
func (md *ManagedDaemon) GetCommand() []string {
return md.command
}
func (md *ManagedDaemon) SetCommand(command []string) {
md.command = make([]string, len(command))
copy(md.command, command)
}
// returns list of mountpoints without the
// agentCommunicationMount and applicationLogMount
func (md *ManagedDaemon) GetFilteredMountPoints() []*MountPoint {
filteredMounts := make([]*MountPoint, len(md.mountPoints))
copy(filteredMounts, md.mountPoints)
return filteredMounts
}
// returns list of mountpoints which (re)integrates
// agentCommunicationMount and applicationLogMount
// these will always include host mount file overrides
func (md *ManagedDaemon) GetMountPoints() []*MountPoint {
allMounts := make([]*MountPoint, len(md.mountPoints))
copy(allMounts, md.mountPoints)
allMounts = append(allMounts, md.agentCommunicationMount)
allMounts = append(allMounts, md.applicationLogMount)
return allMounts
}
func (md *ManagedDaemon) GetEnvironment() map[string]string {
return md.environment
}
func (md *ManagedDaemon) GetLoadedDaemonImageRef() string {
return md.loadedDaemonImageRef
}
func (md *ManagedDaemon) SetLoadedDaemonImageRef(loadedImageRef string) {
md.loadedDaemonImageRef = loadedImageRef
}
func (md *ManagedDaemon) GetHealthCheckTest() []string {
return md.healthCheckTest
}
func (md *ManagedDaemon) GetHealthCheckInterval() time.Duration {
return md.healthCheckInterval
}
func (md *ManagedDaemon) GetHealthCheckTimeout() time.Duration {
return md.healthCheckTimeout
}
func (md *ManagedDaemon) GetHealthCheckRetries() int {
return md.healthCheckRetries
}
func (md *ManagedDaemon) SetHealthCheck(
healthCheckTest []string,
healthCheckInterval time.Duration,
healthCheckTimeout time.Duration,
healthCheckRetries int) {
md.healthCheckInterval = healthCheckInterval
md.healthCheckTimeout = healthCheckTimeout
md.healthCheckRetries = healthCheckRetries
md.healthCheckTest = make([]string, len(healthCheckTest))
copy(md.healthCheckTest, healthCheckTest)
}
// filter mount points for agentCommunicationMount
// set required mounts
// and override host paths in favor of agent defaults
// when a duplicate SourceVolumeID is given, the last Mount wins
func (md *ManagedDaemon) SetMountPoints(mountPoints []*MountPoint) error {
var mountPointMap = make(map[string]*MountPoint)
for _, mp := range mountPoints {
if mp.SourceVolumeID == defaultAgentCommunicationMount {
mp.SourceVolumeHostPath = filepath.Join(defaultAgentCommunicationPathHostRoot, md.imageName) + string(filepath.Separator)
md.agentCommunicationMount = mp
} else if mp.SourceVolumeID == defaultApplicationLogMount {
mp.SourceVolumeHostPath = filepath.Join(defaultApplicationLogPathHostRoot, md.imageName) + string(filepath.Separator)
md.applicationLogMount = mp
} else {
mountPointMap[mp.SourceVolumeID] = mp
}
}
mountResult := []*MountPoint{}
for _, mp := range mountPointMap {
mountResult = append(mountResult, mp)
}
md.mountPoints = mountResult
return nil
}
// Used to set or to update the agentCommunicationMount
func (md *ManagedDaemon) SetAgentCommunicationMount(mp *MountPoint) error {
if mp.SourceVolumeID == defaultAgentCommunicationMount {
mp.SourceVolumeHostPath = filepath.Join(defaultAgentCommunicationPathHostRoot, md.imageName) + string(filepath.Separator)
md.agentCommunicationMount = mp
return nil
} else {
return fmt.Errorf("AgentCommunicationMount %s must have a SourceVolumeID of %s", mp.SourceVolumeID, defaultAgentCommunicationMount)
}
}
// Used to set or to update the applicationLogMount
func (md *ManagedDaemon) SetApplicationLogMount(mp *MountPoint) error {
if mp.SourceVolumeID == defaultApplicationLogMount {
mp.SourceVolumeHostPath = filepath.Join(defaultApplicationLogPathHostRoot, md.imageName) + string(filepath.Separator)
md.applicationLogMount = mp
return nil
} else {
return fmt.Errorf("ApplicationLogMount %s must have a SourceVolumeID of %s", mp.SourceVolumeID, defaultApplicationLogMount)
}
}
func (md *ManagedDaemon) SetEnvironment(environment map[string]string) {
md.environment = make(map[string]string)
for key, val := range environment {
md.environment[key] = val
}
}
func (md *ManagedDaemon) SetPrivileged(isPrivileged bool) {
md.privileged = isPrivileged
}
func (md *ManagedDaemon) GetContainerId() string {
return md.containerId
}
func (md *ManagedDaemon) SetContainerId(containerId string) {
md.containerId = containerId
}
func (md *ManagedDaemon) GetContainerCGroup() string {
return md.containerCGroup
}
func (md *ManagedDaemon) SetContainerCGroup(containerCGroup string) {
md.containerCGroup = containerCGroup
}
func (md *ManagedDaemon) GetNetworkNameSpace() string {
return md.networkNameSpace
}
func (md *ManagedDaemon) SetNetworkNameSpace(networkNameSpace string) {
md.networkNameSpace = networkNameSpace
}
// AddMountPoint will add by MountPoint.SourceVolume
// which is unique to the task and is a required field
// and will throw an error if an existing
// MountPoint.SourceVolume is found
func (md *ManagedDaemon) AddMountPoint(mp *MountPoint) error {
mountIndex := md.GetMountPointIndex(mp)
if mountIndex != -1 {
return fmt.Errorf("MountPoint already exists at index %d", mountIndex)
}
md.mountPoints = append(md.mountPoints, mp)
return nil
}
// UpdateMountPoint will update by
// MountPoint.SourceVolume which is unique to the task
// and will throw an error if the MountPoint.SourceVolume
// is not found
func (md *ManagedDaemon) UpdateMountPointBySourceVolume(mp *MountPoint) error {
mountIndex := md.GetMountPointIndex(mp)
if mountIndex == -1 {
return fmt.Errorf("MountPoint %s not found; will not update", mp.SourceVolume)
}
md.mountPoints[mountIndex] = mp
return nil
}
// UpdateMountPoint will delete by
// MountPoint.SourceVolume which is unique to the task
// and will throw an error if the MountPoint.SourceVolume
// is not found
func (md *ManagedDaemon) DeleteMountPoint(mp *MountPoint) error {
mountIndex := md.GetMountPointIndex(mp)
if mountIndex == -1 {
return fmt.Errorf("MountPoint %s not found; will not delete", mp.SourceVolume)
}
md.mountPoints = append(md.mountPoints[:mountIndex], md.mountPoints[mountIndex+1:]...)
return nil
}
// GetMountPointIndex will return index of a mountpoint or -1
// search by the unique MountPoint.SourceVolume field
func (md *ManagedDaemon) GetMountPointIndex(mp *MountPoint) int {
sourceVolume := mp.SourceVolume
for i, mount := range md.mountPoints {
if mount.SourceVolume == sourceVolume {
return i
}
}
return -1
}
// AddEnvVar will add by envKey
// and will throw an error if an existing
// envKey is found
func (md *ManagedDaemon) AddEnvVar(envKey string, envVal string) error {
_, exists := md.environment[envKey]
if !exists {
md.environment[envKey] = envVal
return nil
}
return fmt.Errorf("EnvKey: %s already exists; will not add EnvVal: %s", envKey, envVal)
}
// Updates environment varable by evnKey
// and will throw an error if the envKey
// is not found
func (md *ManagedDaemon) UpdateEnvVar(envKey string, envVal string) error {
_, ok := md.environment[envKey]
if !ok {
return fmt.Errorf("EnvKey: %s not found; will not update EnvVal: %s", envKey, envVal)
}
md.environment[envKey] = envVal
return nil
}
// Deletes environment variable by envKey
// and will throw an error if the envKey
// is not found
func (md *ManagedDaemon) DeleteEnvVar(envKey string) error {
_, ok := md.environment[envKey]
if !ok {
return fmt.Errorf("EnvKey: %s not found; will not delete", envKey)
}
delete(md.environment, envKey)
return nil
}
// Generates a DockerHealthConfig object from the
// ManagedDaeemon Health Check fields
func (md *ManagedDaemon) GetDockerHealthConfig() *dockercontainer.HealthConfig {
return &dockercontainer.HealthConfig{
Test: md.healthCheckTest,
Interval: md.healthCheckInterval,
Timeout: md.healthCheckTimeout,
Retries: md.healthCheckRetries,
}
}
// Validates that all required fields are present and valid
func (md *ManagedDaemon) IsValidManagedDaemon() bool {
isValid := true
isValid = isValid && (md.agentCommunicationMount != nil)
isValid = isValid && (md.applicationLogMount != nil)
return isValid
}