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 }