<NME> PodTemplate.java
<BEF> package org.csanchez.jenkins.plugins.kubernetes;

import java.io.Serializable;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.DescriptorVisibilityFilter;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.Saveable;
import hudson.model.TaskListener;
import org.apache.commons.lang.StringUtils;
import org.csanchez.jenkins.plugins.kubernetes.model.TemplateEnvVar;
import org.csanchez.jenkins.plugins.kubernetes.pod.retention.PodRetention;
import org.csanchez.jenkins.plugins.kubernetes.pod.yaml.YamlMergeStrategy;
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.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import hudson.Extension;
import hudson.Util;
import hudson.model.labels.LabelAtom;
import hudson.slaves.NodeProperty;
import hudson.util.XStream2;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.KubernetesClient;
import java.io.StringReader;
import jenkins.model.Jenkins;

/**
 * Kubernetes Pod Template
 *
 * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
 */
public class PodTemplate extends AbstractDescribableImpl<PodTemplate> implements Serializable, Saveable {

    private static final long serialVersionUID = 3285310269140845583L;

    private static final String FALLBACK_ARGUMENTS = "${computer.jnlpmac} ${computer.name}";

    private static final String DEFAULT_LABEL = "slave-default";

    private static final Logger LOGGER = Logger.getLogger(PodTemplate.class.getName());

    /**
     * Connection timeout expiration in seconds, default to 1000 seconds
     */
    public static final Integer DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT = Integer
            .getInteger(PodTemplate.class.getName() + ".connectionTimeout", 1000);

    /**
     * Digest function that is used to compute the kubernetes label "jenkins/label-digest"
     * Not used for security.
     */
    protected static MessageDigest getLabelDigestFunction() {
        try {
            return MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            // will never happen, SHA-1 support required on every Java implementation
            e.printStackTrace();
            // throw error to allow variable to be set as final
            throw new AssertionError(e);
        }
    }

    private String id;

    private transient boolean unwrapped;

    private String inheritFrom;

    private String name;

    private String namespace;

    private String image;

    private boolean privileged;

    private Long runAsUser;

    private Long runAsGroup;

    private String supplementalGroups;

    private boolean capOnlyOnAlivePods;

    private boolean alwaysPullImage;

    private String command;

    private String args;

    private String remoteFs;

    private int instanceCap = Integer.MAX_VALUE;

    private int slaveConnectTimeout = DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT;

    private int idleMinutes;

    private int activeDeadlineSeconds;

    private String label;

    /**
     * Set of label atoms this pod template provides in Jenkins. Jenkins indexes them on creation.
     */
    private transient Set<LabelAtom> labelSet;

    private transient Map<String, String> labelsMap;

    private String serviceAccount;

    private String schedulerName;

    private String nodeSelector;

    private Node.Mode nodeUsageMode;

    private String resourceRequestCpu;

    private String resourceRequestMemory;

    private String resourceRequestEphemeralStorage;

    private String resourceLimitCpu;

    private String resourceLimitMemory;

    private String resourceLimitEphemeralStorage;

    private Boolean hostNetwork;

    private WorkspaceVolume workspaceVolume;

    private final List<PodVolume> volumes = new ArrayList<>();

    private List<ContainerTemplate> containers = new ArrayList<>();

    private List<TemplateEnvVar> envVars = new ArrayList<>();
    public PodTemplate(PodTemplate from) {
        XStream2 xs = new XStream2();
        xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);
    }

    @Deprecated
    private Long terminationGracePeriodSeconds;

    /**
     * Persisted yaml fragment
     */
    private String yaml;

    /**
     * List of yaml fragments used for transient pod templates. Never persisted
     */
    private transient List<String> yamls;

    @NonNull
    public String getId() {
        return id;
    }

    public YamlMergeStrategy getYamlMergeStrategy() {
        return yamlMergeStrategy;
    }

    @DataBoundSetter
    public void setYamlMergeStrategy(YamlMergeStrategy yamlMergeStrategy) {
        this.yamlMergeStrategy = yamlMergeStrategy;
    }

    private YamlMergeStrategy yamlMergeStrategy = YamlMergeStrategy.defaultStrategy();

    public Pod getYamlsPod() {
        return yamlMergeStrategy.merge(getYamls());
    }

    private Boolean showRawYaml;

    /**
     * Listener of the run that created this pod template, if applicable
     */
    @CheckForNull
    private transient TaskListener listener;

    @CheckForNull
    private PodRetention podRetention;

    public PodTemplate() {
        this((String) null);
    }

    @DataBoundConstructor
    public PodTemplate(@CheckForNull String id) {
        if (Util.fixEmpty(id) == null) {
            this.id = UUID.randomUUID().toString();
        } else {
            this.id = id;
        }
        recomputeLabelDerivedFields();
    }

    public PodTemplate(PodTemplate from) {
        XStream2 xs = new XStream2();
        xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);
        this.yamls = from.yamls;
        this.listener = from.listener;
        recomputeLabelDerivedFields();
    }

    @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(NoExternalUse.class) // testing only
    PodTemplate(String name, List<? extends PodVolume> volumes, List<? extends ContainerTemplate> containers) {
        this();
        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 = Util.fixEmptyAndTrim(inheritFrom);
    }

    @DataBoundSetter
    public void setName(String name) {
        this.name = Util.fixEmptyAndTrim(name);
    }

    public String getName() {
        return name;
    }

    public String getNamespace() {
        return namespace;
    }

    @DataBoundSetter
    public void setNamespace(String namespace) {
        this.namespace = Util.fixEmptyAndTrim(namespace);
    }

    @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);
    }

    @Deprecated // why would you use this method? It returns the constant "Kubernetes Pod Template".
    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(ContainerTemplate.DEFAULT_WORKING_DIR);
    }

    @DataBoundSetter
    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 setSlaveConnectTimeout(int slaveConnectTimeout) {
        if (slaveConnectTimeout <= 0) {
            LOGGER.log(Level.WARNING, "Agent -> Jenkins connection timeout " +
                    "cannot be <= 0. Falling back to the default value: " +
                    DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT);
            this.slaveConnectTimeout = DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT;
        } else {
            this.slaveConnectTimeout = slaveConnectTimeout;
        }
    }

    public int getSlaveConnectTimeout() {
        if (slaveConnectTimeout == 0)
            return DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT;
        return slaveConnectTimeout;
    }

    @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);
        }
    }

    @DataBoundSetter
    public void setSlaveConnectTimeoutStr(String slaveConnectTimeoutStr) {
        if (StringUtils.isBlank(slaveConnectTimeoutStr)) {
            setSlaveConnectTimeout(DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT);
        } else {
            setSlaveConnectTimeout(Integer.parseInt(slaveConnectTimeoutStr));
        }
    }

    public String getSlaveConnectTimeoutStr() {
        return String.valueOf(slaveConnectTimeout);
    }

    @DataBoundSetter
    public void setIdleMinutes(int i) {
        this.idleMinutes = i;
    }

    public int getIdleMinutes() {
        return idleMinutes;
    }

    @DataBoundSetter
    public void setActiveDeadlineSeconds(int i) {
        this.activeDeadlineSeconds = i;
    }

    public int getActiveDeadlineSeconds() {
        return activeDeadlineSeconds;
    }

    @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);
        }
    }

    @DataBoundSetter
    public void setActiveDeadlineSecondsStr(String activeDeadlineSeconds) {
        if (StringUtils.isBlank(activeDeadlineSeconds)) {
            setActiveDeadlineSeconds(0);
        } else {
            setActiveDeadlineSeconds(Integer.parseInt(activeDeadlineSeconds));
        }
    }

    public String getActiveDeadlineSecondsStr() {
        if (getActiveDeadlineSeconds() == 0) {
            return "";
        } else {
            return String.valueOf(activeDeadlineSeconds);
        }
    }

    public Set<LabelAtom> getLabelSet() {
        return labelSet;
    }

    public Map<String, String> getLabelsMap() {
        return labelsMap;
    }

    static String sanitizeLabel(String input) {
        String label = input;
        int max = 63;
        // Kubernetes limit
        // a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must
        // start and end with an alphanumeric character (e.g. 'MyValue',  or 'my_value',  or '12345', regex used
        // for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')
        if (label.length() > max) {
            label = label.substring(label.length() - max);
        }
        label = label.replaceAll("[^_.a-zA-Z0-9-]", "_").replaceFirst("^[^a-zA-Z0-9]", "x").replaceFirst("[^a-zA-Z0-9]$", "x");
        return label;
    }

    @DataBoundSetter
    public void setLabel(String label) {
        this.label = Util.fixEmptyAndTrim(label);
        recomputeLabelDerivedFields();
    }

    private void recomputeLabelDerivedFields() {
        this.labelSet = Label.parse(label);
        Map<String, String> tempMap = new HashMap<>();
        if (label == null) {
            tempMap.put("jenkins/label", DEFAULT_LABEL);
            tempMap.put("jenkins/label-digest", "0");
        } else {
            MessageDigest labelDigestFunction = getLabelDigestFunction();
            labelDigestFunction.update(label.getBytes(StandardCharsets.UTF_8));
            tempMap.put("jenkins/label", sanitizeLabel(label));
            tempMap.put("jenkins/label-digest", String.format("%040x", new BigInteger(1, labelDigestFunction.digest())));
        }
        labelsMap = Collections.unmodifiableMap(tempMap);
    }

    public String getLabel() {
        return label;
    }

    @DataBoundSetter
    public void setNodeSelector(String nodeSelector) {
        this.nodeSelector = Util.fixEmptyAndTrim(nodeSelector);
    }

    public String getNodeSelector() {
        return nodeSelector;
    }

    @DataBoundSetter
    public void setNodeUsageMode(Node.Mode nodeUsageMode) {
        this.nodeUsageMode = nodeUsageMode == Node.Mode.EXCLUSIVE ? null : nodeUsageMode;
    }

    @DataBoundSetter
    public void setNodeUsageMode(String nodeUsageMode) {
        setNodeUsageMode(Node.Mode.valueOf(nodeUsageMode));
    }

    public Node.Mode getNodeUsageMode() {
        return nodeUsageMode == null ? Node.Mode.EXCLUSIVE : nodeUsageMode;
    }

    @Deprecated
    @DataBoundSetter
    public void setPrivileged(boolean privileged) {
        getFirstContainer().ifPresent((i) -> i.setPrivileged(privileged));
    }

    @Deprecated
    public boolean isPrivileged() {
        return getFirstContainer().map(ContainerTemplate::isPrivileged).orElse(false);
    }

    @DataBoundSetter
    public void setRunAsUser(String runAsUser) {
        this.runAsUser = PodTemplateUtils.parseLong(runAsUser);
    }

    public String getRunAsUser() {
        return runAsUser == null ? null : runAsUser.toString();
    }

    public Long getRunAsUserAsLong() {
        return runAsUser;
    }

    @DataBoundSetter
    public void setRunAsGroup(String runAsGroup) {
        this.runAsGroup = PodTemplateUtils.parseLong(runAsGroup);
    }

    public String getRunAsGroup() {
        return runAsGroup == null ? null : runAsGroup.toString();
    }

    public Long getRunAsGroupAsLong() {
        return runAsGroup;
    }

    @DataBoundSetter
    public void setSupplementalGroups(String supplementalGroups) {
        this.supplementalGroups = Util.fixEmpty(supplementalGroups);
    }

    public String getSupplementalGroups() {
        return this.supplementalGroups;
    }

    @DataBoundSetter
    public void setHostNetwork(Boolean hostNetwork) {
        this.hostNetwork = hostNetwork;
    }

    public boolean isHostNetwork() {
        return isHostNetworkSet() ? hostNetwork.booleanValue() : false;
    }

    public boolean isHostNetworkSet() {
        return hostNetwork != null;
    }

    public String getServiceAccount() {
        return serviceAccount;
    }

    @DataBoundSetter
    public void setServiceAccount(String serviceAccount) {
        this.serviceAccount = Util.fixEmpty(serviceAccount);
    }

    public String getSchedulerName() {
        return schedulerName;
    }

    @DataBoundSetter
    public void setSchedulerName(String schedulerName) {
        this.schedulerName = Util.fixEmpty(schedulerName);
    }

    @Deprecated
    @DataBoundSetter
    public void setAlwaysPullImage(boolean alwaysPullImage) {
        getFirstContainer().ifPresent((i) -> i.setAlwaysPullImage(alwaysPullImage));
    }

    @Deprecated
    public boolean isAlwaysPullImage() {
        return getFirstContainer().map(ContainerTemplate::isAlwaysPullImage).orElse(false);
    }

    @DataBoundSetter
    @Deprecated
    public void setCapOnlyOnAlivePods(boolean capOnlyOnAlivePods) {
        this.capOnlyOnAlivePods = capOnlyOnAlivePods;
    }

    @Deprecated
    public boolean isCapOnlyOnAlivePods() {
        return capOnlyOnAlivePods;
    }

    public List<TemplateEnvVar> getEnvVars() {
        if (envVars == null) {
            return Collections.emptyList();
        }
        return envVars;
    }

    public void addEnvVars(List<TemplateEnvVar> envVars) {
        if (envVars != null) {
            this.envVars.addAll(envVars);
        }
    }

    @DataBoundSetter
    public void setEnvVars(List<TemplateEnvVar> envVars) {
        if (envVars != null) {
            this.envVars.clear();
            this.addEnvVars(envVars);
        }
    }

    public List<PodAnnotation> getAnnotations() {
        if (annotations == null) {
            return Collections.emptyList();
        }
        return annotations;
    }

    public void addAnnotations(List<PodAnnotation> annotations) {
        this.annotations.addAll(annotations);
    }


    @DataBoundSetter
    public void setAnnotations(List<PodAnnotation> annotations) {
        if (annotations != null) {
            this.annotations = new ArrayList<PodAnnotation>();
            this.addAnnotations(annotations);
        }
    }

    public List<PodImagePullSecret> getImagePullSecrets() {
        return imagePullSecrets == null ? Collections.emptyList() : imagePullSecrets;
    }

    public void addImagePullSecrets(List<PodImagePullSecret> imagePullSecrets) {
        this.imagePullSecrets.addAll(imagePullSecrets);
    }

    @DataBoundSetter
    public void setImagePullSecrets(List<PodImagePullSecret> imagePullSecrets) {
        if (imagePullSecrets != null) {
            this.imagePullSecrets.clear();
            this.addImagePullSecrets(imagePullSecrets);
        }
    }

    @DataBoundSetter
    public void setNodeProperties(List<? extends NodeProperty<?>> properties)  {
        this.getNodeProperties().clear();
        this.getNodeProperties().addAll(properties);
    }

    @NonNull
    public PodTemplateToolLocation getNodeProperties(){
        if( this.nodeProperties == null)
            this.nodeProperties = new PodTemplateToolLocation(this);
        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;
    }

    @NonNull
    public WorkspaceVolume getWorkspaceVolume() {
        return workspaceVolume == null ? WorkspaceVolume.getDefault() : workspaceVolume;
    }

    @DataBoundSetter
    public void setWorkspaceVolume(WorkspaceVolume workspaceVolume) {
        this.workspaceVolume = WorkspaceVolume.getDefault().equals(workspaceVolume) ? null : 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;
    }

    /**
     * @return The persisted yaml fragment
     */
    public String getYaml() {
        return yaml;
    }

    @DataBoundSetter
    public void setYaml(String yaml) {
        this.yaml = Util.fixEmpty(yaml);
    }

    @NonNull
    public List<String> getYamls() {
        if (yamls == null || yamls.isEmpty()) {
            if (yaml != null) {
                return Collections.singletonList(yaml);
            } else {
                return Collections.emptyList();
            }
        }
        return yamls;
    }

    @Restricted(NoExternalUse.class)
    List<String> _getYamls() {
        return yamls;
    }

    public void setYamls(List<String> yamls) {
        if (yamls != null) {
            List<String> ys = new ArrayList<>();
            for (String y : yamls) {
                String trimmed = Util.fixEmpty(y);
                if (trimmed != null) {
                    ys.add(trimmed);
                }
            }
            this.yamls = ys;
        } else {
            this.yamls = Collections.emptyList();
        }
    }

    public PodRetention getPodRetention() {
        return podRetention == null ? PodRetention.getPodTemplateDefault() : podRetention;
    }

    @DataBoundSetter
    public void setPodRetention(PodRetention podRetention) {
        this.podRetention = PodRetention.getPodTemplateDefault().equals(podRetention) ? null : podRetention;
    }

    @NonNull
    public TaskListener getListener() {
        return listener == null ? TaskListener.NULL : listener;
    }

    public void setListener(@CheckForNull TaskListener listener) {
        this.listener = listener;
    }

    public Long getTerminationGracePeriodSeconds() {
        return terminationGracePeriodSeconds;
    }

    public void setTerminationGracePeriodSeconds(Long terminationGracePeriodSeconds) {
        this.terminationGracePeriodSeconds = terminationGracePeriodSeconds;
    }

    protected Object readResolve() {
        if (containers == null) {
            // upgrading from 0.8
            containers = new ArrayList<>();
            ContainerTemplate containerTemplate = new ContainerTemplate(KubernetesCloud.JNLP_NAME, this.image);
            containerTemplate.setCommand(command);
            containerTemplate.setArgs(PodTemplateUtils.isNullOrEmpty(args) ? FALLBACK_ARGUMENTS : args);
            containerTemplate.setPrivileged(privileged);
            containerTemplate.setRunAsUser(getRunAsUser());
            containerTemplate.setRunAsGroup(getRunAsGroup());
            containerTemplate.setAlwaysPullImage(alwaysPullImage);
            containerTemplate.setEnvVars(envVars);
            containerTemplate.setResourceLimitMemory(resourceLimitMemory);
            containerTemplate.setResourceLimitCpu(resourceLimitCpu);
            containerTemplate.setResourceLimitEphemeralStorage(resourceLimitEphemeralStorage);
            containerTemplate.setResourceRequestMemory(resourceRequestMemory);
            containerTemplate.setResourceRequestCpu(resourceRequestCpu);
            containerTemplate.setResourceRequestEphemeralStorage(resourceRequestEphemeralStorage);
            containerTemplate.setWorkingDir(remoteFs);
            containers.add(containerTemplate);
        }

        if (annotations == null) {
            annotations = new ArrayList<>();
        }

        // Sanitize empty values
        yaml = Util.fixEmpty(yaml);
        if (yamls != null) {
            // JENKINS-57116 Sanitize empty values
            setYamls(yamls);
            // Migration from storage in yamls field
            if (!yamls.isEmpty()) {
                if (yamls.size() > 1) {
                    LOGGER.log(Level.WARNING, "Found several persisted YAML fragments in pod template " + name + ". Only the first fragment will be considered, others will be ignored.");
                }
                yaml = yamls.get(0);
            }
            yamls = null;
        }

        if (yamlMergeStrategy == null) {
            yamlMergeStrategy = YamlMergeStrategy.defaultStrategy();
        }
        if (id == null) {
            // Use the label and a digest of the current object representation to get the same value every restart if the object isn't saved.
            id = getLabel() + "-" + Util.getDigestOf(toString());
        }
        recomputeLabelDerivedFields();

        return this;
    }

    @Deprecated
    public Pod build(KubernetesClient client, KubernetesSlave slave) {
        return build(slave);
    }

    /**
     * Build a Pod object from a PodTemplate
     *
     * @param slave
     */
    public Pod build(KubernetesSlave slave) {
        return new PodTemplateBuilder(this, slave).build();
    }

    /**
     * @deprecated Use {@code Serialization.asYaml(build(KubernetesSlave))} instead.
     */
    @Deprecated
    public String getDescriptionForLogging() {
        return String.format("Agent specification [%s] (%s): %n%s",
                getName(),
                getLabel(),
                getContainersDescriptionForLogging());
    }

    boolean isShowRawYamlSet() {
        return showRawYaml != null;
    }

    public boolean isShowRawYaml() {
        return isShowRawYamlSet() ? showRawYaml.booleanValue() : true;
    }

    @DataBoundSetter
    public void setShowRawYaml(boolean showRawYaml) {
        this.showRawYaml = Boolean.valueOf(showRawYaml);
    }

    void setUnwrapped(boolean unwrapped) {
        this.unwrapped = unwrapped;
    }

    boolean isUnwrapped() {
        return unwrapped;
    }

    private String getContainersDescriptionForLogging() {
        List<ContainerTemplate> containers = getContainers();
        StringBuilder sb = new StringBuilder();
        for (ContainerTemplate ct : containers) {
            sb.append("* [").append(ct.getName()).append("] ").append(ct.getImage());
            StringBuilder optional = new StringBuilder();
            optionalField(optional, "resourceRequestCpu", ct.getResourceRequestCpu());
            optionalField(optional, "resourceRequestMemory", ct.getResourceRequestMemory());
            optionalField(optional, "resourceRequestEphemeralStorage", ct.getResourceRequestEphemeralStorage());
            optionalField(optional, "resourceLimitCpu", ct.getResourceLimitCpu());
            optionalField(optional, "resourceLimitMemory", ct.getResourceLimitMemory());
            optionalField(optional, "resourceLimitEphemeralStorage", ct.getResourceLimitEphemeralStorage());
            if (optional.length() > 0) {
                sb.append("(").append(optional).append(")");
            }
            sb.append("\n");
        }
        if (isShowRawYaml()) {
            for (String yaml : getYamls()) {
                sb.append("yaml:\n")
                    .append(yaml)
                    .append("\n");
            }
        }
        return sb.toString();
    }

    private void optionalField(StringBuilder builder, String label, String value) {
        if (StringUtils.isNotBlank(value)) {
            if (builder.length() > 0) {
                builder.append(", ");
            }
            builder.append(label).append(": ").append(value);
        }
    }

    /**
     * Empty implementation of Saveable interface. This interface is used for DescribableList implementation
     */
    @Override
    public void save()  { }

    @Extension
    public static class DescriptorImpl extends Descriptor<PodTemplate> {

        static final String[] STRING_FIELDS = {
                "activeDeadlineSeconds",
                "idleMinutes",
                "instanceCap",
                "slaveConnectTimeout",
        };

        public DescriptorImpl() {
            for (String field : STRING_FIELDS) {
                addHelpFileRedirect(field + "Str", PodTemplate.class, field);
            }
        }

        @Override
        public String getDisplayName() {
            return "Kubernetes Pod Template";
        }

        @SuppressWarnings("unused") // Used by jelly
        @Restricted(DoNotUse.class) // Used by jelly
        public List<? extends Descriptor> getEnvVarsDescriptors() {
            return DescriptorVisibilityFilter.apply(null, Jenkins.get().getDescriptorList(TemplateEnvVar.class));
        }

        @SuppressWarnings("unused") // Used by jelly
        @Restricted(DoNotUse.class) // Used by jelly
        public WorkspaceVolume getDefaultWorkspaceVolume() {
            return WorkspaceVolume.getDefault();
        }

        @SuppressWarnings("unused") // Used by jelly
        @Restricted(DoNotUse.class) // Used by jelly
        public Descriptor getDefaultPodRetention() {
            return Jenkins.get().getDescriptor(PodRetention.getPodTemplateDefault().getClass());
        }

        @SuppressWarnings("unused") // Used by jelly
        @Restricted(DoNotUse.class) // Used by jelly
        public YamlMergeStrategy getDefaultYamlMergeStrategy() {
            return YamlMergeStrategy.defaultStrategy();
        }
    }

    @Override
    public String toString() {
        return "PodTemplate{" +
                (id == null ? "" : "id='" + id + '\'') +
                (inheritFrom == null ? "" : ", inheritFrom='" + inheritFrom + '\'') +
                (name == null ? "" : ", name='" + name + '\'') +
                (namespace == null ? "" : ", namespace='" + namespace + '\'') +
                (image == null ? "" : ", image='" + image + '\'') +
                (!privileged ? "" : ", privileged=" + privileged) +
                (runAsUser == null ? "" : ", runAsUser=" + runAsUser) +
                (runAsGroup == null ? "" : ", runAsGroup=" + runAsGroup) +
                (!isHostNetwork() ? "" : ", hostNetwork=" + hostNetwork) +
                (!alwaysPullImage ? "" : ", alwaysPullImage=" + alwaysPullImage) +
                (command == null ? "" : ", command='" + command + '\'') +
                (args == null ? "" : ", args='" + args + '\'') +
                (remoteFs == null ? "" : ", remoteFs='" + remoteFs + '\'') +
                (instanceCap == Integer.MAX_VALUE ? "" : ", instanceCap=" + instanceCap) +
                (slaveConnectTimeout == DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT ? "" : ", slaveConnectTimeout=" + slaveConnectTimeout) +
                (idleMinutes == 0 ? "" : ", idleMinutes=" + idleMinutes) +
                (activeDeadlineSeconds == 0 ? "" : ", activeDeadlineSeconds=" + activeDeadlineSeconds) +
                (label == null ? "" : ", label='" + label + '\'') +
                (serviceAccount == null ? "" : ", serviceAccount='" + serviceAccount + '\'') +
                (schedulerName == null ? "" : ", schedulerName='" + schedulerName + '\'') +
                (nodeSelector == null ? "" : ", nodeSelector='" + nodeSelector + '\'') +
                (nodeUsageMode == null ? "" : ", nodeUsageMode=" + nodeUsageMode) +
                (resourceRequestCpu == null ? "" : ", resourceRequestCpu='" + resourceRequestCpu + '\'') +
                (resourceRequestMemory == null ? "" : ", resourceRequestMemory='" + resourceRequestMemory + '\'') +
                (resourceRequestEphemeralStorage == null ? "" : ", resourceRequestEphemeralStorage='" + resourceRequestEphemeralStorage + '\'') +
                (resourceLimitCpu == null ? "" : ", resourceLimitCpu='" + resourceLimitCpu + '\'') +
                (resourceLimitMemory == null ? "" : ", resourceLimitMemory='" + resourceLimitMemory + '\'') +
                (resourceLimitEphemeralStorage == null ? "" : ", resourceLimitEphemeralStorage='" + resourceLimitEphemeralStorage + '\'') +
                (workspaceVolume == null ? "" : ", workspaceVolume='" + workspaceVolume + '\'') +
                (podRetention == null ? "" : ", podRetention='" + podRetention + '\'') +
                (volumes == null || volumes.isEmpty() ? "" : ", volumes=" + volumes) +
                (containers == null || containers.isEmpty() ? "" : ", containers=" + containers) +
                (envVars == null || envVars.isEmpty() ? "" : ", envVars=" + envVars) +
                (annotations == null || annotations.isEmpty() ? "" : ", annotations=" + annotations) +
                (imagePullSecrets == null || imagePullSecrets.isEmpty() ? "" : ", imagePullSecrets=" + imagePullSecrets) +
                (nodeProperties == null || nodeProperties.isEmpty() ? "" : ", nodeProperties=" + nodeProperties) +
                (yamls == null || yamls.isEmpty() ? "" : ", yamls=" + yamls) +
                (!unwrapped ? "" : ", unwrapped=" + unwrapped) +
                '}';
    }
}

<MSG> PodTemplate.yamls is transient, so also needs to be copied.

<DFF> @@ -162,6 +162,7 @@ public class PodTemplate extends AbstractDescribableImpl<PodTemplate> implements
     public PodTemplate(PodTemplate from) {
         XStream2 xs = new XStream2();
         xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);
+        this.yamls = from.yamls;
     }
 
     @Deprecated

{'commit': 'diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java\nindex 50563758..c3ff28a7 100644\n--- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java\n+++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java\n@@ -162,6 +162,7 @@ public class PodTemplate extends AbstractDescribableImpl<PodTemplate> implements\n     public PodTemplate(PodTemplate from) {\n         XStream2 xs = new XStream2();\n         xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);\n+        this.yamls = from.yamls;\n     }\n \n     @Deprecated\n', 'commit_subject': 'PodTemplate.yamls is transient, so also needs to be copied.', 'repo_name': 'jenkinsci/kubernetes-plugin', 'committer': {'name': 'Jesse Glick', 'email': 'f03f7b035106bba3da9b565be6f0115dc0d88f87@cloudbees.com', 'time_sec': '1569874951', 'tz_offset': '-240', 'date': {'seconds': '1569874951'}}, 'commit_tree': '7b7584d140f74516883f59152fb69118e5baf141', 'commit_message': 'PodTemplate.yamls is transient, so also needs to be copied.\n', 'language_name': 'Java', 'license': 'apache-2.0', 'hunks': '@@ -162,6 +162,7 @@ public class PodTemplate extends AbstractDescribableImpl<PodTemplate> implements\n     public PodTemplate(PodTemplate from) {\n         XStream2 xs = new XStream2();\n         xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);\n+        this.yamls = from.yamls;\n     }\n \n     @Deprecated\n', 'addition_count': 1, 'deletion_count': 0, 'src_file': 'a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java', 'tgt_file': 'b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/PodTemplate.java', 'file_extension': '.java', 'before_file': 'package org.csanchez.jenkins.plugins.kubernetes;\n\nimport java.io.Serializable;\nimport java.math.BigInteger;\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport edu.umd.cs.findbugs.annotations.CheckForNull;\nimport edu.umd.cs.findbugs.annotations.NonNull;\nimport hudson.model.AbstractDescribableImpl;\nimport hudson.model.Descriptor;\nimport hudson.model.DescriptorVisibilityFilter;\nimport hudson.model.Label;\nimport hudson.model.Node;\nimport hudson.model.Saveable;\nimport hudson.model.TaskListener;\nimport org.apache.commons.lang.StringUtils;\nimport org.csanchez.jenkins.plugins.kubernetes.model.TemplateEnvVar;\nimport org.csanchez.jenkins.plugins.kubernetes.pod.retention.PodRetention;\nimport org.csanchez.jenkins.plugins.kubernetes.pod.yaml.YamlMergeStrategy;\nimport org.csanchez.jenkins.plugins.kubernetes.volumes.PodVolume;\nimport org.csanchez.jenkins.plugins.kubernetes.volumes.workspace.WorkspaceVolume;\nimport org.kohsuke.accmod.Restricted;\nimport org.kohsuke.accmod.restrictions.DoNotUse;\nimport org.kohsuke.accmod.restrictions.NoExternalUse;\nimport org.kohsuke.stapler.DataBoundConstructor;\nimport org.kohsuke.stapler.DataBoundSetter;\n\nimport hudson.Extension;\nimport hudson.Util;\nimport hudson.model.labels.LabelAtom;\nimport hudson.slaves.NodeProperty;\nimport hudson.util.XStream2;\nimport io.fabric8.kubernetes.api.model.Pod;\nimport io.fabric8.kubernetes.client.KubernetesClient;\nimport java.io.StringReader;\nimport jenkins.model.Jenkins;\n\n/**\n * Kubernetes Pod Template\n *\n * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>\n */\npublic class PodTemplate extends AbstractDescribableImpl<PodTemplate> implements Serializable, Saveable {\n\n    private static final long serialVersionUID = 3285310269140845583L;\n\n    private static final String FALLBACK_ARGUMENTS = "${computer.jnlpmac} ${computer.name}";\n\n    private static final String DEFAULT_LABEL = "slave-default";\n\n    private static final Logger LOGGER = Logger.getLogger(PodTemplate.class.getName());\n\n    /**\n     * Connection timeout expiration in seconds, default to 1000 seconds\n     */\n    public static final Integer DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT = Integer\n            .getInteger(PodTemplate.class.getName() + ".connectionTimeout", 1000);\n\n    /**\n     * Digest function that is used to compute the kubernetes label "jenkins/label-digest"\n     * Not used for security.\n     */\n    protected static MessageDigest getLabelDigestFunction() {\n        try {\n            return MessageDigest.getInstance("SHA-1");\n        } catch (NoSuchAlgorithmException e) {\n            // will never happen, SHA-1 support required on every Java implementation\n            e.printStackTrace();\n            // throw error to allow variable to be set as final\n            throw new AssertionError(e);\n        }\n    }\n\n    private String id;\n\n    private transient boolean unwrapped;\n\n    private String inheritFrom;\n\n    private String name;\n\n    private String namespace;\n\n    private String image;\n\n    private boolean privileged;\n\n    private Long runAsUser;\n\n    private Long runAsGroup;\n\n    private String supplementalGroups;\n\n    private boolean capOnlyOnAlivePods;\n\n    private boolean alwaysPullImage;\n\n    private String command;\n\n    private String args;\n\n    private String remoteFs;\n\n    private int instanceCap = Integer.MAX_VALUE;\n\n    private int slaveConnectTimeout = DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT;\n\n    private int idleMinutes;\n\n    private int activeDeadlineSeconds;\n\n    private String label;\n\n    /**\n     * Set of label atoms this pod template provides in Jenkins. Jenkins indexes them on creation.\n     */\n    private transient Set<LabelAtom> labelSet;\n\n    private transient Map<String, String> labelsMap;\n\n    private String serviceAccount;\n\n    private String schedulerName;\n\n    private String nodeSelector;\n\n    private Node.Mode nodeUsageMode;\n\n    private String resourceRequestCpu;\n\n    private String resourceRequestMemory;\n\n    private String resourceRequestEphemeralStorage;\n\n    private String resourceLimitCpu;\n\n    private String resourceLimitMemory;\n\n    private String resourceLimitEphemeralStorage;\n\n    private Boolean hostNetwork;\n\n    private WorkspaceVolume workspaceVolume;\n\n    private final List<PodVolume> volumes = new ArrayList<>();\n\n    private List<ContainerTemplate> containers = new ArrayList<>();\n\n    private List<TemplateEnvVar> envVars = new ArrayList<>();\n    public PodTemplate(PodTemplate from) {\n        XStream2 xs = new XStream2();\n        xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);\n    }\n\n    @Deprecated\n    private Long terminationGracePeriodSeconds;\n\n    /**\n     * Persisted yaml fragment\n     */\n    private String yaml;\n\n    /**\n     * List of yaml fragments used for transient pod templates. Never persisted\n     */\n    private transient List<String> yamls;\n\n    @NonNull\n    public String getId() {\n        return id;\n    }\n\n    public YamlMergeStrategy getYamlMergeStrategy() {\n        return yamlMergeStrategy;\n    }\n\n    @DataBoundSetter\n    public void setYamlMergeStrategy(YamlMergeStrategy yamlMergeStrategy) {\n        this.yamlMergeStrategy = yamlMergeStrategy;\n    }\n\n    private YamlMergeStrategy yamlMergeStrategy = YamlMergeStrategy.defaultStrategy();\n\n    public Pod getYamlsPod() {\n        return yamlMergeStrategy.merge(getYamls());\n    }\n\n    private Boolean showRawYaml;\n\n    /**\n     * Listener of the run that created this pod template, if applicable\n     */\n    @CheckForNull\n    private transient TaskListener listener;\n\n    @CheckForNull\n    private PodRetention podRetention;\n\n    public PodTemplate() {\n        this((String) null);\n    }\n\n    @DataBoundConstructor\n    public PodTemplate(@CheckForNull String id) {\n        if (Util.fixEmpty(id) == null) {\n            this.id = UUID.randomUUID().toString();\n        } else {\n            this.id = id;\n        }\n        recomputeLabelDerivedFields();\n    }\n\n    public PodTemplate(PodTemplate from) {\n        XStream2 xs = new XStream2();\n        xs.unmarshal(XStream2.getDefaultDriver().createReader(new StringReader(xs.toXML(from))), this);\n        this.yamls = from.yamls;\n        this.listener = from.listener;\n        recomputeLabelDerivedFields();\n    }\n\n    @Deprecated\n    public PodTemplate(String image, List<? extends PodVolume> volumes) {\n        this(null, image, volumes);\n    }\n\n    @Deprecated\n    PodTemplate(String name, String image, List<? extends PodVolume> volumes) {\n        this(name, volumes, Collections.emptyList());\n        if (image != null) {\n            getContainers().add(new ContainerTemplate(name, image));\n        }\n    }\n\n    @Restricted(NoExternalUse.class) // testing only\n    PodTemplate(String name, List<? extends PodVolume> volumes, List<? extends ContainerTemplate> containers) {\n        this();\n        this.name = name;\n        this.volumes.addAll(volumes);\n        this.containers.addAll(containers);\n    }\n\n    private Optional<ContainerTemplate> getFirstContainer() {\n        return Optional.ofNullable(getContainers().isEmpty() ? null : getContainers().get(0));\n    }\n\n    public String getInheritFrom() {\n        return inheritFrom;\n    }\n\n    @DataBoundSetter\n    public void setInheritFrom(String inheritFrom) {\n        this.inheritFrom = Util.fixEmptyAndTrim(inheritFrom);\n    }\n\n    @DataBoundSetter\n    public void setName(String name) {\n        this.name = Util.fixEmptyAndTrim(name);\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    @DataBoundSetter\n    public void setNamespace(String namespace) {\n        this.namespace = Util.fixEmptyAndTrim(namespace);\n    }\n\n    @Deprecated\n    public String getImage() {\n        return getFirstContainer().map(ContainerTemplate::getImage).orElse(null);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setCommand(String command) {\n        getFirstContainer().ifPresent((i) -> i.setCommand(command));\n    }\n\n    @Deprecated\n    public String getCommand() {\n        return getFirstContainer().map(ContainerTemplate::getCommand).orElse(null);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setArgs(String args) {\n        getFirstContainer().ifPresent((i) -> i.setArgs(args));\n    }\n\n    @Deprecated\n    public String getArgs() {\n        return getFirstContainer().map(ContainerTemplate::getArgs).orElse(null);\n    }\n\n    @Deprecated // why would you use this method? It returns the constant "Kubernetes Pod Template".\n    public String getDisplayName() {\n        return "Kubernetes Pod Template";\n    }\n\n    @DataBoundSetter\n    @Deprecated\n    public void setRemoteFs(String remoteFs) {\n        getFirstContainer().ifPresent((i) -> i.setWorkingDir(remoteFs));\n    }\n\n    @Deprecated\n    public String getRemoteFs() {\n        return getFirstContainer().map(ContainerTemplate::getWorkingDir).orElse(ContainerTemplate.DEFAULT_WORKING_DIR);\n    }\n\n    @DataBoundSetter\n    public void setInstanceCap(int instanceCap) {\n        if (instanceCap < 0) {\n            this.instanceCap = Integer.MAX_VALUE;\n        } else {\n            this.instanceCap = instanceCap;\n        }\n    }\n\n    public int getInstanceCap() {\n        return instanceCap;\n    }\n\n    @DataBoundSetter\n    public void setSlaveConnectTimeout(int slaveConnectTimeout) {\n        if (slaveConnectTimeout <= 0) {\n            LOGGER.log(Level.WARNING, "Agent -> Jenkins connection timeout " +\n                    "cannot be <= 0. Falling back to the default value: " +\n                    DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT);\n            this.slaveConnectTimeout = DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT;\n        } else {\n            this.slaveConnectTimeout = slaveConnectTimeout;\n        }\n    }\n\n    public int getSlaveConnectTimeout() {\n        if (slaveConnectTimeout == 0)\n            return DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT;\n        return slaveConnectTimeout;\n    }\n\n    @DataBoundSetter\n    public void setInstanceCapStr(String instanceCapStr) {\n        if (StringUtils.isBlank(instanceCapStr)) {\n            setInstanceCap(Integer.MAX_VALUE);\n        } else {\n            setInstanceCap(Integer.parseInt(instanceCapStr));\n        }\n    }\n\n    public String getInstanceCapStr() {\n        if (getInstanceCap() == Integer.MAX_VALUE) {\n            return "";\n        } else {\n            return String.valueOf(instanceCap);\n        }\n    }\n\n    @DataBoundSetter\n    public void setSlaveConnectTimeoutStr(String slaveConnectTimeoutStr) {\n        if (StringUtils.isBlank(slaveConnectTimeoutStr)) {\n            setSlaveConnectTimeout(DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT);\n        } else {\n            setSlaveConnectTimeout(Integer.parseInt(slaveConnectTimeoutStr));\n        }\n    }\n\n    public String getSlaveConnectTimeoutStr() {\n        return String.valueOf(slaveConnectTimeout);\n    }\n\n    @DataBoundSetter\n    public void setIdleMinutes(int i) {\n        this.idleMinutes = i;\n    }\n\n    public int getIdleMinutes() {\n        return idleMinutes;\n    }\n\n    @DataBoundSetter\n    public void setActiveDeadlineSeconds(int i) {\n        this.activeDeadlineSeconds = i;\n    }\n\n    public int getActiveDeadlineSeconds() {\n        return activeDeadlineSeconds;\n    }\n\n    @DataBoundSetter\n    public void setIdleMinutesStr(String idleMinutes) {\n        if (StringUtils.isBlank(idleMinutes)) {\n            setIdleMinutes(0);\n        } else {\n            setIdleMinutes(Integer.parseInt(idleMinutes));\n        }\n    }\n\n    public String getIdleMinutesStr() {\n        if (getIdleMinutes() == 0) {\n            return "";\n        } else {\n            return String.valueOf(idleMinutes);\n        }\n    }\n\n    @DataBoundSetter\n    public void setActiveDeadlineSecondsStr(String activeDeadlineSeconds) {\n        if (StringUtils.isBlank(activeDeadlineSeconds)) {\n            setActiveDeadlineSeconds(0);\n        } else {\n            setActiveDeadlineSeconds(Integer.parseInt(activeDeadlineSeconds));\n        }\n    }\n\n    public String getActiveDeadlineSecondsStr() {\n        if (getActiveDeadlineSeconds() == 0) {\n            return "";\n        } else {\n            return String.valueOf(activeDeadlineSeconds);\n        }\n    }\n\n    public Set<LabelAtom> getLabelSet() {\n        return labelSet;\n    }\n\n    public Map<String, String> getLabelsMap() {\n        return labelsMap;\n    }\n\n    static String sanitizeLabel(String input) {\n        String label = input;\n        int max = 63;\n        // Kubernetes limit\n        // a valid label must be an empty string or consist of alphanumeric characters, \'-\', \'_\' or \'.\', and must\n        // start and end with an alphanumeric character (e.g. \'MyValue\',  or \'my_value\',  or \'12345\', regex used\n        // for validation is \'(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?\')\n        if (label.length() > max) {\n            label = label.substring(label.length() - max);\n        }\n        label = label.replaceAll("[^_.a-zA-Z0-9-]", "_").replaceFirst("^[^a-zA-Z0-9]", "x").replaceFirst("[^a-zA-Z0-9]$", "x");\n        return label;\n    }\n\n    @DataBoundSetter\n    public void setLabel(String label) {\n        this.label = Util.fixEmptyAndTrim(label);\n        recomputeLabelDerivedFields();\n    }\n\n    private void recomputeLabelDerivedFields() {\n        this.labelSet = Label.parse(label);\n        Map<String, String> tempMap = new HashMap<>();\n        if (label == null) {\n            tempMap.put("jenkins/label", DEFAULT_LABEL);\n            tempMap.put("jenkins/label-digest", "0");\n        } else {\n            MessageDigest labelDigestFunction = getLabelDigestFunction();\n            labelDigestFunction.update(label.getBytes(StandardCharsets.UTF_8));\n            tempMap.put("jenkins/label", sanitizeLabel(label));\n            tempMap.put("jenkins/label-digest", String.format("%040x", new BigInteger(1, labelDigestFunction.digest())));\n        }\n        labelsMap = Collections.unmodifiableMap(tempMap);\n    }\n\n    public String getLabel() {\n        return label;\n    }\n\n    @DataBoundSetter\n    public void setNodeSelector(String nodeSelector) {\n        this.nodeSelector = Util.fixEmptyAndTrim(nodeSelector);\n    }\n\n    public String getNodeSelector() {\n        return nodeSelector;\n    }\n\n    @DataBoundSetter\n    public void setNodeUsageMode(Node.Mode nodeUsageMode) {\n        this.nodeUsageMode = nodeUsageMode == Node.Mode.EXCLUSIVE ? null : nodeUsageMode;\n    }\n\n    @DataBoundSetter\n    public void setNodeUsageMode(String nodeUsageMode) {\n        setNodeUsageMode(Node.Mode.valueOf(nodeUsageMode));\n    }\n\n    public Node.Mode getNodeUsageMode() {\n        return nodeUsageMode == null ? Node.Mode.EXCLUSIVE : nodeUsageMode;\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setPrivileged(boolean privileged) {\n        getFirstContainer().ifPresent((i) -> i.setPrivileged(privileged));\n    }\n\n    @Deprecated\n    public boolean isPrivileged() {\n        return getFirstContainer().map(ContainerTemplate::isPrivileged).orElse(false);\n    }\n\n    @DataBoundSetter\n    public void setRunAsUser(String runAsUser) {\n        this.runAsUser = PodTemplateUtils.parseLong(runAsUser);\n    }\n\n    public String getRunAsUser() {\n        return runAsUser == null ? null : runAsUser.toString();\n    }\n\n    public Long getRunAsUserAsLong() {\n        return runAsUser;\n    }\n\n    @DataBoundSetter\n    public void setRunAsGroup(String runAsGroup) {\n        this.runAsGroup = PodTemplateUtils.parseLong(runAsGroup);\n    }\n\n    public String getRunAsGroup() {\n        return runAsGroup == null ? null : runAsGroup.toString();\n    }\n\n    public Long getRunAsGroupAsLong() {\n        return runAsGroup;\n    }\n\n    @DataBoundSetter\n    public void setSupplementalGroups(String supplementalGroups) {\n        this.supplementalGroups = Util.fixEmpty(supplementalGroups);\n    }\n\n    public String getSupplementalGroups() {\n        return this.supplementalGroups;\n    }\n\n    @DataBoundSetter\n    public void setHostNetwork(Boolean hostNetwork) {\n        this.hostNetwork = hostNetwork;\n    }\n\n    public boolean isHostNetwork() {\n        return isHostNetworkSet() ? hostNetwork.booleanValue() : false;\n    }\n\n    public boolean isHostNetworkSet() {\n        return hostNetwork != null;\n    }\n\n    public String getServiceAccount() {\n        return serviceAccount;\n    }\n\n    @DataBoundSetter\n    public void setServiceAccount(String serviceAccount) {\n        this.serviceAccount = Util.fixEmpty(serviceAccount);\n    }\n\n    public String getSchedulerName() {\n        return schedulerName;\n    }\n\n    @DataBoundSetter\n    public void setSchedulerName(String schedulerName) {\n        this.schedulerName = Util.fixEmpty(schedulerName);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setAlwaysPullImage(boolean alwaysPullImage) {\n        getFirstContainer().ifPresent((i) -> i.setAlwaysPullImage(alwaysPullImage));\n    }\n\n    @Deprecated\n    public boolean isAlwaysPullImage() {\n        return getFirstContainer().map(ContainerTemplate::isAlwaysPullImage).orElse(false);\n    }\n\n    @DataBoundSetter\n    @Deprecated\n    public void setCapOnlyOnAlivePods(boolean capOnlyOnAlivePods) {\n        this.capOnlyOnAlivePods = capOnlyOnAlivePods;\n    }\n\n    @Deprecated\n    public boolean isCapOnlyOnAlivePods() {\n        return capOnlyOnAlivePods;\n    }\n\n    public List<TemplateEnvVar> getEnvVars() {\n        if (envVars == null) {\n            return Collections.emptyList();\n        }\n        return envVars;\n    }\n\n    public void addEnvVars(List<TemplateEnvVar> envVars) {\n        if (envVars != null) {\n            this.envVars.addAll(envVars);\n        }\n    }\n\n    @DataBoundSetter\n    public void setEnvVars(List<TemplateEnvVar> envVars) {\n        if (envVars != null) {\n            this.envVars.clear();\n            this.addEnvVars(envVars);\n        }\n    }\n\n    public List<PodAnnotation> getAnnotations() {\n        if (annotations == null) {\n            return Collections.emptyList();\n        }\n        return annotations;\n    }\n\n    public void addAnnotations(List<PodAnnotation> annotations) {\n        this.annotations.addAll(annotations);\n    }\n\n\n    @DataBoundSetter\n    public void setAnnotations(List<PodAnnotation> annotations) {\n        if (annotations != null) {\n            this.annotations = new ArrayList<PodAnnotation>();\n            this.addAnnotations(annotations);\n        }\n    }\n\n    public List<PodImagePullSecret> getImagePullSecrets() {\n        return imagePullSecrets == null ? Collections.emptyList() : imagePullSecrets;\n    }\n\n    public void addImagePullSecrets(List<PodImagePullSecret> imagePullSecrets) {\n        this.imagePullSecrets.addAll(imagePullSecrets);\n    }\n\n    @DataBoundSetter\n    public void setImagePullSecrets(List<PodImagePullSecret> imagePullSecrets) {\n        if (imagePullSecrets != null) {\n            this.imagePullSecrets.clear();\n            this.addImagePullSecrets(imagePullSecrets);\n        }\n    }\n\n    @DataBoundSetter\n    public void setNodeProperties(List<? extends NodeProperty<?>> properties)  {\n        this.getNodeProperties().clear();\n        this.getNodeProperties().addAll(properties);\n    }\n\n    @NonNull\n    public PodTemplateToolLocation getNodeProperties(){\n        if( this.nodeProperties == null)\n            this.nodeProperties = new PodTemplateToolLocation(this);\n        return nodeProperties;\n    }\n\n\n    @Deprecated\n    public String getResourceRequestMemory() {\n        return getFirstContainer().map(ContainerTemplate::getResourceRequestMemory).orElse(null);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setResourceRequestMemory(String resourceRequestMemory) {\n        getFirstContainer().ifPresent((i) -> i.setResourceRequestMemory(resourceRequestMemory));\n    }\n\n    @Deprecated\n    public String getResourceLimitCpu() {\n        return getFirstContainer().map(ContainerTemplate::getResourceLimitCpu).orElse(null);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setResourceLimitCpu(String resourceLimitCpu) {\n        getFirstContainer().ifPresent((i) -> i.setResourceLimitCpu(resourceLimitCpu));\n    }\n\n        @Deprecated\n    public String getResourceLimitMemory() {\n        return getFirstContainer().map(ContainerTemplate::getResourceLimitMemory).orElse(null);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setResourceLimitMemory(String resourceLimitMemory) {\n        getFirstContainer().ifPresent((i) -> i.setResourceLimitMemory(resourceLimitMemory));\n    }\n\n    @Deprecated\n    public String getResourceRequestCpu() {\n        return getFirstContainer().map(ContainerTemplate::getResourceRequestCpu).orElse(null);\n    }\n\n    @Deprecated\n    @DataBoundSetter\n    public void setResourceRequestCpu(String resourceRequestCpu) {\n        getFirstContainer().ifPresent((i) -> i.setResourceRequestCpu(resourceRequestCpu));\n    }\n\n    @DataBoundSetter\n    public void setVolumes(@NonNull List<PodVolume> items) {\n        synchronized (this.volumes) {\n            this.volumes.clear();\n            this.volumes.addAll(items);\n        }\n    }\n\n    @NonNull\n    public List<PodVolume> getVolumes() {\n        if (volumes == null) {\n            return Collections.emptyList();\n        }\n        return volumes;\n    }\n\n    @NonNull\n    public WorkspaceVolume getWorkspaceVolume() {\n        return workspaceVolume == null ? WorkspaceVolume.getDefault() : workspaceVolume;\n    }\n\n    @DataBoundSetter\n    public void setWorkspaceVolume(WorkspaceVolume workspaceVolume) {\n        this.workspaceVolume = WorkspaceVolume.getDefault().equals(workspaceVolume) ? null : workspaceVolume;\n    }\n\n    @DataBoundSetter\n    public void setContainers(@NonNull List<ContainerTemplate> items) {\n        synchronized (this.containers) {\n            this.containers.clear();\n            this.containers.addAll(items);\n        }\n    }\n\n    @NonNull\n    public List<ContainerTemplate> getContainers() {\n        if (containers == null) {\n            return Collections.emptyList();\n        }\n        return containers;\n    }\n\n    /**\n     * @return The persisted yaml fragment\n     */\n    public String getYaml() {\n        return yaml;\n    }\n\n    @DataBoundSetter\n    public void setYaml(String yaml) {\n        this.yaml = Util.fixEmpty(yaml);\n    }\n\n    @NonNull\n    public List<String> getYamls() {\n        if (yamls == null || yamls.isEmpty()) {\n            if (yaml != null) {\n                return Collections.singletonList(yaml);\n            } else {\n                return Collections.emptyList();\n            }\n        }\n        return yamls;\n    }\n\n    @Restricted(NoExternalUse.class)\n    List<String> _getYamls() {\n        return yamls;\n    }\n\n    public void setYamls(List<String> yamls) {\n        if (yamls != null) {\n            List<String> ys = new ArrayList<>();\n            for (String y : yamls) {\n                String trimmed = Util.fixEmpty(y);\n                if (trimmed != null) {\n                    ys.add(trimmed);\n                }\n            }\n            this.yamls = ys;\n        } else {\n            this.yamls = Collections.emptyList();\n        }\n    }\n\n    public PodRetention getPodRetention() {\n        return podRetention == null ? PodRetention.getPodTemplateDefault() : podRetention;\n    }\n\n    @DataBoundSetter\n    public void setPodRetention(PodRetention podRetention) {\n        this.podRetention = PodRetention.getPodTemplateDefault().equals(podRetention) ? null : podRetention;\n    }\n\n    @NonNull\n    public TaskListener getListener() {\n        return listener == null ? TaskListener.NULL : listener;\n    }\n\n    public void setListener(@CheckForNull TaskListener listener) {\n        this.listener = listener;\n    }\n\n    public Long getTerminationGracePeriodSeconds() {\n        return terminationGracePeriodSeconds;\n    }\n\n    public void setTerminationGracePeriodSeconds(Long terminationGracePeriodSeconds) {\n        this.terminationGracePeriodSeconds = terminationGracePeriodSeconds;\n    }\n\n    protected Object readResolve() {\n        if (containers == null) {\n            // upgrading from 0.8\n            containers = new ArrayList<>();\n            ContainerTemplate containerTemplate = new ContainerTemplate(KubernetesCloud.JNLP_NAME, this.image);\n            containerTemplate.setCommand(command);\n            containerTemplate.setArgs(PodTemplateUtils.isNullOrEmpty(args) ? FALLBACK_ARGUMENTS : args);\n            containerTemplate.setPrivileged(privileged);\n            containerTemplate.setRunAsUser(getRunAsUser());\n            containerTemplate.setRunAsGroup(getRunAsGroup());\n            containerTemplate.setAlwaysPullImage(alwaysPullImage);\n            containerTemplate.setEnvVars(envVars);\n            containerTemplate.setResourceLimitMemory(resourceLimitMemory);\n            containerTemplate.setResourceLimitCpu(resourceLimitCpu);\n            containerTemplate.setResourceLimitEphemeralStorage(resourceLimitEphemeralStorage);\n            containerTemplate.setResourceRequestMemory(resourceRequestMemory);\n            containerTemplate.setResourceRequestCpu(resourceRequestCpu);\n            containerTemplate.setResourceRequestEphemeralStorage(resourceRequestEphemeralStorage);\n            containerTemplate.setWorkingDir(remoteFs);\n            containers.add(containerTemplate);\n        }\n\n        if (annotations == null) {\n            annotations = new ArrayList<>();\n        }\n\n        // Sanitize empty values\n        yaml = Util.fixEmpty(yaml);\n        if (yamls != null) {\n            // JENKINS-57116 Sanitize empty values\n            setYamls(yamls);\n            // Migration from storage in yamls field\n            if (!yamls.isEmpty()) {\n                if (yamls.size() > 1) {\n                    LOGGER.log(Level.WARNING, "Found several persisted YAML fragments in pod template " + name + ". Only the first fragment will be considered, others will be ignored.");\n                }\n                yaml = yamls.get(0);\n            }\n            yamls = null;\n        }\n\n        if (yamlMergeStrategy == null) {\n            yamlMergeStrategy = YamlMergeStrategy.defaultStrategy();\n        }\n        if (id == null) {\n            // Use the label and a digest of the current object representation to get the same value every restart if the object isn\'t saved.\n            id = getLabel() + "-" + Util.getDigestOf(toString());\n        }\n        recomputeLabelDerivedFields();\n\n        return this;\n    }\n\n    @Deprecated\n    public Pod build(KubernetesClient client, KubernetesSlave slave) {\n        return build(slave);\n    }\n\n    /**\n     * Build a Pod object from a PodTemplate\n     *\n     * @param slave\n     */\n    public Pod build(KubernetesSlave slave) {\n        return new PodTemplateBuilder(this, slave).build();\n    }\n\n    /**\n     * @deprecated Use {@code Serialization.asYaml(build(KubernetesSlave))} instead.\n     */\n    @Deprecated\n    public String getDescriptionForLogging() {\n        return String.format("Agent specification [%s] (%s): %n%s",\n                getName(),\n                getLabel(),\n                getContainersDescriptionForLogging());\n    }\n\n    boolean isShowRawYamlSet() {\n        return showRawYaml != null;\n    }\n\n    public boolean isShowRawYaml() {\n        return isShowRawYamlSet() ? showRawYaml.booleanValue() : true;\n    }\n\n    @DataBoundSetter\n    public void setShowRawYaml(boolean showRawYaml) {\n        this.showRawYaml = Boolean.valueOf(showRawYaml);\n    }\n\n    void setUnwrapped(boolean unwrapped) {\n        this.unwrapped = unwrapped;\n    }\n\n    boolean isUnwrapped() {\n        return unwrapped;\n    }\n\n    private String getContainersDescriptionForLogging() {\n        List<ContainerTemplate> containers = getContainers();\n        StringBuilder sb = new StringBuilder();\n        for (ContainerTemplate ct : containers) {\n            sb.append("* [").append(ct.getName()).append("] ").append(ct.getImage());\n            StringBuilder optional = new StringBuilder();\n            optionalField(optional, "resourceRequestCpu", ct.getResourceRequestCpu());\n            optionalField(optional, "resourceRequestMemory", ct.getResourceRequestMemory());\n            optionalField(optional, "resourceRequestEphemeralStorage", ct.getResourceRequestEphemeralStorage());\n            optionalField(optional, "resourceLimitCpu", ct.getResourceLimitCpu());\n            optionalField(optional, "resourceLimitMemory", ct.getResourceLimitMemory());\n            optionalField(optional, "resourceLimitEphemeralStorage", ct.getResourceLimitEphemeralStorage());\n            if (optional.length() > 0) {\n                sb.append("(").append(optional).append(")");\n            }\n            sb.append("\\n");\n        }\n        if (isShowRawYaml()) {\n            for (String yaml : getYamls()) {\n                sb.append("yaml:\\n")\n                    .append(yaml)\n                    .append("\\n");\n            }\n        }\n        return sb.toString();\n    }\n\n    private void optionalField(StringBuilder builder, String label, String value) {\n        if (StringUtils.isNotBlank(value)) {\n            if (builder.length() > 0) {\n                builder.append(", ");\n            }\n            builder.append(label).append(": ").append(value);\n        }\n    }\n\n    /**\n     * Empty implementation of Saveable interface. This interface is used for DescribableList implementation\n     */\n    @Override\n    public void save()  { }\n\n    @Extension\n    public static class DescriptorImpl extends Descriptor<PodTemplate> {\n\n        static final String[] STRING_FIELDS = {\n                "activeDeadlineSeconds",\n                "idleMinutes",\n                "instanceCap",\n                "slaveConnectTimeout",\n        };\n\n        public DescriptorImpl() {\n            for (String field : STRING_FIELDS) {\n                addHelpFileRedirect(field + "Str", PodTemplate.class, field);\n            }\n        }\n\n        @Override\n        public String getDisplayName() {\n            return "Kubernetes Pod Template";\n        }\n\n        @SuppressWarnings("unused") // Used by jelly\n        @Restricted(DoNotUse.class) // Used by jelly\n        public List<? extends Descriptor> getEnvVarsDescriptors() {\n            return DescriptorVisibilityFilter.apply(null, Jenkins.get().getDescriptorList(TemplateEnvVar.class));\n        }\n\n        @SuppressWarnings("unused") // Used by jelly\n        @Restricted(DoNotUse.class) // Used by jelly\n        public WorkspaceVolume getDefaultWorkspaceVolume() {\n            return WorkspaceVolume.getDefault();\n        }\n\n        @SuppressWarnings("unused") // Used by jelly\n        @Restricted(DoNotUse.class) // Used by jelly\n        public Descriptor getDefaultPodRetention() {\n            return Jenkins.get().getDescriptor(PodRetention.getPodTemplateDefault().getClass());\n        }\n\n        @SuppressWarnings("unused") // Used by jelly\n        @Restricted(DoNotUse.class) // Used by jelly\n        public YamlMergeStrategy getDefaultYamlMergeStrategy() {\n            return YamlMergeStrategy.defaultStrategy();\n        }\n    }\n\n    @Override\n    public String toString() {\n        return "PodTemplate{" +\n                (id == null ? "" : "id=\'" + id + \'\\\'\') +\n                (inheritFrom == null ? "" : ", inheritFrom=\'" + inheritFrom + \'\\\'\') +\n                (name == null ? "" : ", name=\'" + name + \'\\\'\') +\n                (namespace == null ? "" : ", namespace=\'" + namespace + \'\\\'\') +\n                (image == null ? "" : ", image=\'" + image + \'\\\'\') +\n                (!privileged ? "" : ", privileged=" + privileged) +\n                (runAsUser == null ? "" : ", runAsUser=" + runAsUser) +\n                (runAsGroup == null ? "" : ", runAsGroup=" + runAsGroup) +\n                (!isHostNetwork() ? "" : ", hostNetwork=" + hostNetwork) +\n                (!alwaysPullImage ? "" : ", alwaysPullImage=" + alwaysPullImage) +\n                (command == null ? "" : ", command=\'" + command + \'\\\'\') +\n                (args == null ? "" : ", args=\'" + args + \'\\\'\') +\n                (remoteFs == null ? "" : ", remoteFs=\'" + remoteFs + \'\\\'\') +\n                (instanceCap == Integer.MAX_VALUE ? "" : ", instanceCap=" + instanceCap) +\n                (slaveConnectTimeout == DEFAULT_SLAVE_JENKINS_CONNECTION_TIMEOUT ? "" : ", slaveConnectTimeout=" + slaveConnectTimeout) +\n                (idleMinutes == 0 ? "" : ", idleMinutes=" + idleMinutes) +\n                (activeDeadlineSeconds == 0 ? "" : ", activeDeadlineSeconds=" + activeDeadlineSeconds) +\n                (label == null ? "" : ", label=\'" + label + \'\\\'\') +\n                (serviceAccount == null ? "" : ", serviceAccount=\'" + serviceAccount + \'\\\'\') +\n                (schedulerName == null ? "" : ", schedulerName=\'" + schedulerName + \'\\\'\') +\n                (nodeSelector == null ? "" : ", nodeSelector=\'" + nodeSelector + \'\\\'\') +\n                (nodeUsageMode == null ? "" : ", nodeUsageMode=" + nodeUsageMode) +\n                (resourceRequestCpu == null ? "" : ", resourceRequestCpu=\'" + resourceRequestCpu + \'\\\'\') +\n                (resourceRequestMemory == null ? "" : ", resourceRequestMemory=\'" + resourceRequestMemory + \'\\\'\') +\n                (resourceRequestEphemeralStorage == null ? "" : ", resourceRequestEphemeralStorage=\'" + resourceRequestEphemeralStorage + \'\\\'\') +\n                (resourceLimitCpu == null ? "" : ", resourceLimitCpu=\'" + resourceLimitCpu + \'\\\'\') +\n                (resourceLimitMemory == null ? "" : ", resourceLimitMemory=\'" + resourceLimitMemory + \'\\\'\') +\n                (resourceLimitEphemeralStorage == null ? "" : ", resourceLimitEphemeralStorage=\'" + resourceLimitEphemeralStorage + \'\\\'\') +\n                (workspaceVolume == null ? "" : ", workspaceVolume=\'" + workspaceVolume + \'\\\'\') +\n                (podRetention == null ? "" : ", podRetention=\'" + podRetention + \'\\\'\') +\n                (volumes == null || volumes.isEmpty() ? "" : ", volumes=" + volumes) +\n                (containers == null || containers.isEmpty() ? "" : ", containers=" + containers) +\n                (envVars == null || envVars.isEmpty() ? "" : ", envVars=" + envVars) +\n                (annotations == null || annotations.isEmpty() ? "" : ", annotations=" + annotations) +\n                (imagePullSecrets == null || imagePullSecrets.isEmpty() ? "" : ", imagePullSecrets=" + imagePullSecrets) +\n                (nodeProperties == null || nodeProperties.isEmpty() ? "" : ", nodeProperties=" + nodeProperties) +\n                (yamls == null || yamls.isEmpty() ? "" : ", yamls=" + yamls) +\n                (!unwrapped ? "" : ", unwrapped=" + unwrapped) +\n                \'}\';\n    }\n}\n', 'message': 'PodTemplate.yamls is transient, so also needs to be copied.\n', 'author_name': 'Jesse Glick', 'lang': 'java'}