Java tutorial
/* * The MIT License * * Copyright (c) 2015, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ package com.cloudbees.jenkins.plugins.amazonecs; import com.amazonaws.services.ecs.model.ContainerDefinition; import com.amazonaws.services.ecs.model.HostEntry; import com.amazonaws.services.ecs.model.Volume; import com.amazonaws.services.ecs.model.HostVolumeProperties; import com.amazonaws.services.ecs.model.MountPoint; import com.amazonaws.services.ecs.model.KeyValuePair; import com.amazonaws.services.ecs.model.RegisterTaskDefinitionRequest; import hudson.Extension; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.model.Label; import hudson.model.labels.LabelAtom; import hudson.util.FormValidation; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.servlet.ServletException; import java.io.IOException; import java.util.*; /** * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a> */ public class ECSTaskTemplate extends AbstractDescribableImpl<ECSTaskTemplate> { /** * Template Name */ @Nonnull private final String templateName; /** * White-space separated list of {@link hudson.model.Node} labels. * * @see Label */ @CheckForNull private final String label; /** * Docker image * @see ContainerDefinition#withImage(String) */ @Nonnull private final String image; /** * Slave remote FS */ @Nullable private final String remoteFSRoot; /** * The number of MiB of memory reserved for the Docker container. If your * container attempts to exceed the memory allocated here, the container * is killed by ECS. * * @see ContainerDefinition#withMemory(Integer) */ private final int memory; /** * The soft limit (in MiB) of memory to reserve for the container. When * system memory is under contention, Docker attempts to keep the container * memory to this soft limit; however, your container can consume more * memory when it needs to, up to either the hard limit specified with the * memory parameter (if applicable), or all of the available memory on the * container instance, whichever comes first. * * @see ContainerDefinition#withMemoryReservation(Integer) */ private final int memoryReservation; /* a hint to ECSService regarding whether it can ask AWS to make a new container or not */ public int getMemoryConstraint() { if (this.memoryReservation > 0) { return this.memoryReservation; } return this.memory; } /** * The number of <code>cpu</code> units reserved for the container. A * container instance has 1,024 <code>cpu</code> units for every CPU * core. This parameter specifies the minimum amount of CPU to reserve * for a container, and containers share unallocated CPU units with other * containers on the instance with the same ratio as their allocated * amount. * * @see ContainerDefinition#withCpu(Integer) */ private final int cpu; /** * Space delimited list of Docker dns search domains * * @see ContainerDefinition#withDnsSearchDomains(Collection) */ @CheckForNull private String dnsSearchDomains; /** * Space delimited list of Docker entry points * * @see ContainerDefinition#withEntryPoint(String...) */ @CheckForNull private String entrypoint; /** * ARN of the IAM role to use for the slave ECS task * * @see RegisterTaskDefinitionRequest#withTaskRoleArn(String) */ @CheckForNull private String taskrole; /** JVM arguments to start slave.jar */ @CheckForNull private String jvmArgs; /** Container mount points, imported from volumes */ private List<MountPointEntry> mountPoints; /** * Indicates whether the container should run in privileged mode */ private final boolean privileged; private List<EnvironmentEntry> environments; private List<ExtraHostEntry> extraHosts; /** * The log configuration specification for the container. * This parameter maps to LogConfig in the Create a container section of * the Docker Remote API and the --log-driver option to docker run. * Valid log drivers are displayed in the LogConfiguration data type. * This parameter requires version 1.18 of the Docker Remote API or greater * on your container instance. To check the Docker Remote API version on * your container instance, log into your container instance and run the * following command: sudo docker version | grep "Server API version" * The Amazon ECS container agent running on a container instance must * register the logging drivers available on that instance with the * ECS_AVAILABLE_LOGGING_DRIVERS environment variable before containers * placed on that instance can use these log configuration options. * For more information, see Amazon ECS Container Agent Configuration * in the Amazon EC2 Container Service Developer Guide. */ @CheckForNull private String logDriver; private List<LogDriverOption> logDriverOptions; @DataBoundConstructor public ECSTaskTemplate(@Nonnull String templateName, @Nullable String label, @Nonnull String image, @Nullable String remoteFSRoot, int memory, int memoryReservation, int cpu, boolean privileged, @Nullable List<LogDriverOption> logDriverOptions, @Nullable List<EnvironmentEntry> environments, @Nullable List<ExtraHostEntry> extraHosts, @Nullable List<MountPointEntry> mountPoints) { // If the template name is empty we will add a default name and a // random element that will help to find it later when we want to delete it. this.templateName = templateName.isEmpty() ? "jenkinsTask-" + UUID.randomUUID().toString() : templateName; this.label = label; this.image = image; this.remoteFSRoot = remoteFSRoot; this.memory = memory; this.memoryReservation = memoryReservation; this.cpu = cpu; this.privileged = privileged; this.logDriverOptions = logDriverOptions; this.environments = environments; this.extraHosts = extraHosts; this.mountPoints = mountPoints; } @DataBoundSetter public void setTaskrole(String taskRoleArn) { this.taskrole = StringUtils.trimToNull(taskRoleArn); } @DataBoundSetter public void setEntrypoint(String entrypoint) { this.entrypoint = StringUtils.trimToNull(entrypoint); } @DataBoundSetter public void setJvmArgs(String jvmArgs) { this.jvmArgs = StringUtils.trimToNull(jvmArgs); } @DataBoundSetter public void setLogDriver(String logDriver) { this.logDriver = StringUtils.trimToNull(logDriver); } @DataBoundSetter public void setDnsSearchDomains(String dnsSearchDomains) { this.dnsSearchDomains = StringUtils.trimToNull(dnsSearchDomains); } public String getLabel() { return label; } public String getImage() { return image; } public String getRemoteFSRoot() { return remoteFSRoot; } public int getMemory() { return memory; } public int getMemoryReservation() { return memoryReservation; } public int getCpu() { return cpu; } public String getDnsSearchDomains() { return dnsSearchDomains; } public String getEntrypoint() { return entrypoint; } public String getTaskrole() { return taskrole; } public String getJvmArgs() { return jvmArgs; } public boolean getPrivileged() { return privileged; } public String getLogDriver() { return logDriver; } public String getTemplateName() { return templateName; } public static class LogDriverOption extends AbstractDescribableImpl<LogDriverOption> { public String name, value; @DataBoundConstructor public LogDriverOption(String name, String value) { this.name = name; this.value = value; } @Override public String toString() { return "LogDriverOption{" + name + ": " + value + "}"; } @Extension public static class DescriptorImpl extends Descriptor<LogDriverOption> { @Override public String getDisplayName() { return "logDriverOption"; } } } public List<LogDriverOption> getLogDriverOptions() { return logDriverOptions; } Map<String, String> getLogDriverOptionsMap() { if (null == logDriverOptions || logDriverOptions.isEmpty()) { return null; } Map<String, String> options = new HashMap<String, String>(); for (LogDriverOption logDriverOption : logDriverOptions) { String name = logDriverOption.name; String value = logDriverOption.value; if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) { continue; } options.put(name, value); } return options; } public List<EnvironmentEntry> getEnvironments() { return environments; } public List<ExtraHostEntry> getExtraHosts() { return extraHosts; } Collection<KeyValuePair> getEnvironmentKeyValuePairs() { if (null == environments || environments.isEmpty()) { return null; } Collection<KeyValuePair> items = new ArrayList<KeyValuePair>(); for (EnvironmentEntry environment : environments) { String name = environment.name; String value = environment.value; if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) { continue; } items.add(new KeyValuePair().withName(name).withValue(value)); } return items; } Collection<HostEntry> getExtraHostEntries() { if (null == extraHosts || extraHosts.isEmpty()) { return null; } Collection<HostEntry> items = new ArrayList<HostEntry>(); for (ExtraHostEntry extrahost : extraHosts) { String ipAddress = extrahost.ipAddress; String hostname = extrahost.hostname; if (StringUtils.isEmpty(ipAddress) || StringUtils.isEmpty(hostname)) { continue; } items.add(new HostEntry().withIpAddress(ipAddress).withHostname(hostname)); } return items; } public List<MountPointEntry> getMountPoints() { return mountPoints; } Collection<Volume> getVolumeEntries() { Collection<Volume> vols = new LinkedList<Volume>(); if (null != mountPoints) { for (MountPointEntry mount : mountPoints) { String name = mount.name; String sourcePath = mount.sourcePath; HostVolumeProperties hostVolume = new HostVolumeProperties(); if (StringUtils.isEmpty(name)) continue; if (!StringUtils.isEmpty(sourcePath)) hostVolume.setSourcePath(sourcePath); vols.add(new Volume().withName(name).withHost(hostVolume)); } } return vols; } Collection<MountPoint> getMountPointEntries() { if (null == mountPoints || mountPoints.isEmpty()) return null; Collection<MountPoint> mounts = new ArrayList<MountPoint>(); for (MountPointEntry mount : mountPoints) { String src = mount.name; String path = mount.containerPath; Boolean ro = mount.readOnly; if (StringUtils.isEmpty(src) || StringUtils.isEmpty(path)) continue; mounts.add(new MountPoint().withSourceVolume(src).withContainerPath(path).withReadOnly(ro)); } return mounts; } public static class EnvironmentEntry extends AbstractDescribableImpl<EnvironmentEntry> { public String name, value; @DataBoundConstructor public EnvironmentEntry(String name, String value) { this.name = name; this.value = value; } @Override public String toString() { return "EnvironmentEntry{" + name + ": " + value + "}"; } @Extension public static class DescriptorImpl extends Descriptor<EnvironmentEntry> { @Override public String getDisplayName() { return "EnvironmentEntry"; } } } public static class ExtraHostEntry extends AbstractDescribableImpl<ExtraHostEntry> { public String ipAddress, hostname; @DataBoundConstructor public ExtraHostEntry(String ipAddress, String hostname) { this.ipAddress = ipAddress; this.hostname = hostname; } @Override public String toString() { return "ExtraHostEntry{" + ipAddress + ": " + hostname + "}"; } @Extension public static class DescriptorImpl extends Descriptor<ExtraHostEntry> { @Override public String getDisplayName() { return "ExtraHostEntry"; } } } public static class MountPointEntry extends AbstractDescribableImpl<MountPointEntry> { public String name, sourcePath, containerPath; public Boolean readOnly; @DataBoundConstructor public MountPointEntry(String name, String sourcePath, String containerPath, Boolean readOnly) { this.name = name; this.sourcePath = sourcePath; this.containerPath = containerPath; this.readOnly = readOnly; } @Override public String toString() { return "MountPointEntry{name:" + name + ", sourcePath:" + sourcePath + ", containerPath:" + containerPath + ", readOnly:" + readOnly + "}"; } @Extension public static class DescriptorImpl extends Descriptor<MountPointEntry> { @Override public String getDisplayName() { return "MountPointEntry"; } } } public Set<LabelAtom> getLabelSet() { return Label.parse(label); } public String getDisplayName() { return "ECS Slave " + label; } @Extension public static class DescriptorImpl extends Descriptor<ECSTaskTemplate> { private static String TEMPLATE_NAME_PATTERN = "[a-z|A-Z|0-9|_|-]{1,127}"; @Override public String getDisplayName() { return Messages.Template(); } public FormValidation doCheckTemplateName(@QueryParameter String value) throws IOException, ServletException { if (value.length() > 0 && value.length() <= 127 && value.matches(TEMPLATE_NAME_PATTERN)) { return FormValidation.ok(); } return FormValidation.error( "Up to 127 letters (uppercase and lowercase), numbers, hyphens, and underscores are allowed"); } /* we validate both memory and memoryReservation fields to the same rules */ public FormValidation doCheckMemory(@QueryParameter("memory") int memory, @QueryParameter("memoryReservation") int memoryReservation) throws IOException, ServletException { return validateMemorySettings(memory, memoryReservation); } public FormValidation doCheckMemoryReservation(@QueryParameter("memory") int memory, @QueryParameter("memoryReservation") int memoryReservation) throws IOException, ServletException { return validateMemorySettings(memory, memoryReservation); } private FormValidation validateMemorySettings(int memory, int memoryReservation) { if (memory < 0 || memoryReservation < 0) { return FormValidation.error("memory and/or memoryReservation must be 0 or a positive integer"); } if (memory == 0 && memoryReservation == 0) { return FormValidation.error("at least one of memory or memoryReservation are required to be > 0"); } if (memory > 0 && memoryReservation > 0) { if (memory <= memoryReservation) { return FormValidation .error("memory must be greater than memoryReservation if both are specified"); } } return FormValidation.ok(); } } }