package org.csanchez.jenkins.plugins.kubernetes; import hudson.tools.ToolLocationNodeProperty; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; import javax.annotation.Nonnull; import org.apache.commons.lang.StringUtils; import org.csanchez.jenkins.plugins.kubernetes.volumes.PodVolume; import org.csanchez.jenkins.plugins.kubernetes.volumes.workspace.WorkspaceVolume; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import com.google.common.base.Strings; import hudson.Extension; import hudson.Util; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.model.Label; import hudson.model.labels.LabelAtom; /** * Kubernetes Pod Template * * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a> */ public class PodTemplate extends AbstractDescribableImpl<PodTemplate> implements Serializable { private static final long serialVersionUID = 3285310269140845583L; private static final String FALLBACK_ARGUMENTS = "${computer.jnlpmac} ${computer.name}"; private String inheritFrom; private String name; private String image; private boolean privileged; private boolean alwaysPullImage; private String command; private String args; private String remoteFs; private int instanceCap = Integer.MAX_VALUE; private int idleMinutes; private String label; private String serviceAccount; private String nodeSelector; private String resourceRequestCpu; private String resourceRequestMemory; private String resourceLimitCpu; private String resourceLimitMemory; private boolean customWorkspaceVolumeEnabled; private WorkspaceVolume workspaceVolume; private final List<PodVolume> volumes = new ArrayList<PodVolume>(); private List<ContainerTemplate> containers = new ArrayList<ContainerTemplate>(); private final List<PodEnvVar> envVars = new ArrayList<PodEnvVar>(); private List<PodAnnotation> annotations = new ArrayList<PodAnnotation>(); private final List<PodImagePullSecret> imagePullSecrets = new ArrayList<PodImagePullSecret>(); private transient List<ToolLocationNodeProperty> nodeProperties; @DataBoundConstructor public PodTemplate() { } public PodTemplate(PodTemplate from) { this.setAnnotations(from.getAnnotations()); this.setContainers(from.getContainers()); this.setImagePullSecrets(from.getImagePullSecrets()); this.setInstanceCap(from.getInstanceCap()); this.setLabel(from.getLabel()); this.setName(from.getName()); this.setInheritFrom(from.getInheritFrom()); this.setNodeSelector(from.getNodeSelector()); this.setServiceAccount(from.getServiceAccount()); this.setVolumes(from.getVolumes()); this.setWorkspaceVolume(from.getWorkspaceVolume()); } @Deprecated public PodTemplate(String image, List<? extends PodVolume> volumes) { this(null, image, volumes); } @Deprecated PodTemplate(String name, String image, List<? extends PodVolume> volumes) { this(name, volumes, Collections.emptyList()); if (image != null) { getContainers().add(new ContainerTemplate(name, image)); } } @Restricted(DoNotUse.class) // testing only PodTemplate(String name, List<? extends PodVolume> volumes, List<? extends ContainerTemplate> containers) { this.name = name; this.volumes.addAll(volumes); this.containers.addAll(containers); } private Optional<ContainerTemplate> getFirstContainer() { return Optional.ofNullable(getContainers().isEmpty() ? null : getContainers().get(0)); } public String getInheritFrom() { return inheritFrom; } @DataBoundSetter public void setInheritFrom(String inheritFrom) { this.inheritFrom = inheritFrom; } @DataBoundSetter public void setName(String name) { this.name = name; } public String getName() { return name; } @Deprecated public String getImage() { return getFirstContainer().map(ContainerTemplate::getImage).orElse(null); } @Deprecated @DataBoundSetter public void setCommand(String command) { getFirstContainer().ifPresent((i) -> i.setCommand(command)); } @Deprecated public String getCommand() { return getFirstContainer().map(ContainerTemplate::getCommand).orElse(null); } @Deprecated @DataBoundSetter public void setArgs(String args) { getFirstContainer().ifPresent((i) -> i.setArgs(args)); } @Deprecated public String getArgs() { return getFirstContainer().map(ContainerTemplate::getArgs).orElse(null); } public String getDisplayName() { return "Kubernetes Pod Template"; } @DataBoundSetter @Deprecated public void setRemoteFs(String remoteFs) { getFirstContainer().ifPresent((i) -> i.setWorkingDir(remoteFs)); } @Deprecated public String getRemoteFs() { return getFirstContainer().map(ContainerTemplate::getWorkingDir).orElse(null); } public void setInstanceCap(int instanceCap) { if (instanceCap <= 0) { this.instanceCap = Integer.MAX_VALUE; } else { this.instanceCap = instanceCap; } } public int getInstanceCap() { return instanceCap; } @DataBoundSetter public void setInstanceCapStr(String instanceCapStr) { if (StringUtils.isBlank(instanceCapStr)) { setInstanceCap(Integer.MAX_VALUE); } else { setInstanceCap(Integer.parseInt(instanceCapStr)); } } public String getInstanceCapStr() { if (getInstanceCap() == Integer.MAX_VALUE) { return ""; } else { return String.valueOf(instanceCap); } } public void setIdleMinutes(int i) { this.idleMinutes = i; } public int getIdleMinutes() { return idleMinutes; } @DataBoundSetter public void setIdleMinutesStr(String idleMinutes) { if (StringUtils.isBlank(idleMinutes)) { setIdleMinutes(0); } else { setIdleMinutes(Integer.parseInt(idleMinutes)); } } public String getIdleMinutesStr() { if (getIdleMinutes() == 0) { return ""; } else { return String.valueOf(idleMinutes); } } public Set<LabelAtom> getLabelSet() { return Label.parse(label); } @DataBoundSetter public void setLabel(String label) { this.label = label; } public String getLabel() { return label; } @DataBoundSetter public void setNodeSelector(String nodeSelector) { this.nodeSelector = nodeSelector; } public String getNodeSelector() { return nodeSelector; } @Deprecated @DataBoundSetter public void setPrivileged(boolean privileged) { getFirstContainer().ifPresent((i) -> i.setPrivileged(privileged)); } @Deprecated public boolean isPrivileged() { return getFirstContainer().map(ContainerTemplate::isPrivileged).orElse(false); } public String getServiceAccount() { return serviceAccount; } @DataBoundSetter public void setServiceAccount(String serviceAccount) { this.serviceAccount = Util.fixEmpty(serviceAccount); } @Deprecated @DataBoundSetter public void setAlwaysPullImage(boolean alwaysPullImage) { getFirstContainer().ifPresent((i) -> i.setAlwaysPullImage(alwaysPullImage)); } @Deprecated public boolean isAlwaysPullImage() { return getFirstContainer().map(ContainerTemplate::isAlwaysPullImage).orElse(false); } public List<PodEnvVar> getEnvVars() { if (envVars == null) { return Collections.emptyList(); } return envVars; } @DataBoundSetter public void setEnvVars(List<PodEnvVar> envVars) { if (envVars != null) { this.envVars.addAll(envVars); } } public List<PodAnnotation> getAnnotations() { if (annotations == null) { return Collections.emptyList(); } return annotations; } @DataBoundSetter public void setAnnotations(List<PodAnnotation> annotations) { this.annotations.addAll(annotations); } public List<PodImagePullSecret> getImagePullSecrets() { if (imagePullSecrets == null) { return Collections.emptyList(); } return imagePullSecrets; } @DataBoundSetter public void setImagePullSecrets(List<PodImagePullSecret> imagePullSecrets) { this.imagePullSecrets.addAll(imagePullSecrets); } @DataBoundSetter public void setNodeProperties(List<ToolLocationNodeProperty> nodeProperties){ this.nodeProperties = nodeProperties; } @Nonnull public List<ToolLocationNodeProperty> getNodeProperties(){ if (nodeProperties == null) { return Collections.emptyList(); } return nodeProperties; } @Deprecated public String getResourceRequestMemory() { return getFirstContainer().map(ContainerTemplate::getResourceRequestMemory).orElse(null); } @Deprecated @DataBoundSetter public void setResourceRequestMemory(String resourceRequestMemory) { getFirstContainer().ifPresent((i) -> i.setResourceRequestMemory(resourceRequestMemory)); } @Deprecated public String getResourceLimitCpu() { return getFirstContainer().map(ContainerTemplate::getResourceLimitCpu).orElse(null); } @Deprecated @DataBoundSetter public void setResourceLimitCpu(String resourceLimitCpu) { getFirstContainer().ifPresent((i) -> i.setResourceLimitCpu(resourceLimitCpu)); } @Deprecated public String getResourceLimitMemory() { return getFirstContainer().map(ContainerTemplate::getResourceLimitMemory).orElse(null); } @Deprecated @DataBoundSetter public void setResourceLimitMemory(String resourceLimitMemory) { getFirstContainer().ifPresent((i) -> i.setResourceLimitMemory(resourceLimitMemory)); } @Deprecated public String getResourceRequestCpu() { return getFirstContainer().map(ContainerTemplate::getResourceRequestCpu).orElse(null); } @Deprecated @DataBoundSetter public void setResourceRequestCpu(String resourceRequestCpu) { getFirstContainer().ifPresent((i) -> i.setResourceRequestCpu(resourceRequestCpu)); } @DataBoundSetter public void setVolumes(@Nonnull List<PodVolume> items) { synchronized (this.volumes) { this.volumes.clear(); this.volumes.addAll(items); } } @Nonnull public List<PodVolume> getVolumes() { if (volumes == null) { return Collections.emptyList(); } return volumes; } public boolean isCustomWorkspaceVolumeEnabled() { return customWorkspaceVolumeEnabled; } @DataBoundSetter public void setCustomWorkspaceVolumeEnabled(boolean customWorkspaceVolumeEnabled) { this.customWorkspaceVolumeEnabled = customWorkspaceVolumeEnabled; } public WorkspaceVolume getWorkspaceVolume() { return workspaceVolume; } @DataBoundSetter public void setWorkspaceVolume(WorkspaceVolume workspaceVolume) { this.workspaceVolume = workspaceVolume; } @DataBoundSetter public void setContainers(@Nonnull List<ContainerTemplate> items) { synchronized (this.containers) { this.containers.clear(); this.containers.addAll(items); } } @Nonnull public List<ContainerTemplate> getContainers() { if (containers == null) { return Collections.emptyList(); } return containers; } @SuppressWarnings("deprecation") protected Object readResolve() { if (containers == null) { // upgrading from 0.8 containers = new ArrayList<ContainerTemplate>(); ContainerTemplate containerTemplate = new ContainerTemplate(KubernetesCloud.JNLP_NAME, this.image); containerTemplate.setCommand(command); containerTemplate.setArgs(Strings.isNullOrEmpty(args) ? FALLBACK_ARGUMENTS : args); containerTemplate.setPrivileged(privileged); containerTemplate.setAlwaysPullImage(alwaysPullImage); containerTemplate.setEnvVars(PodEnvVar.asContainerEnvVar(envVars)); containerTemplate.setResourceRequestMemory(resourceRequestMemory); containerTemplate.setResourceLimitCpu(resourceLimitCpu); containerTemplate.setResourceLimitMemory(resourceLimitMemory); containerTemplate.setResourceRequestCpu(resourceRequestCpu); containerTemplate.setWorkingDir(remoteFs); containers.add(containerTemplate); } if (annotations == null) { annotations = new ArrayList<>(); } return this; } @Extension public static class DescriptorImpl extends Descriptor<PodTemplate> { @Override public String getDisplayName() { return "Kubernetes Pod Template"; } } }