All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.microbean.helm.maven.AbstractReleaseMojo Maven / Gradle / Ivy

/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
 *
 * Copyright © 2017-2018 microBean.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package org.microbean.helm.maven;

import java.io.IOException;

import java.util.Map;
import java.util.Objects;

import java.util.concurrent.Callable;

import java.util.regex.Matcher;

import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

import org.apache.maven.plugin.logging.Log;

import org.apache.maven.plugins.annotations.Parameter;

import org.microbean.helm.ReleaseManager;
import org.microbean.helm.Tiller;

/**
 * An {@link AbstractHelmMojo} that provides other Helm-related
 * mojo
 * implementations the ability to work with a {@link ReleaseManager}.
 *
 * @author Laird Nelson
 *
 * @see #execute(Callable)
 */
public abstract class AbstractReleaseMojo extends AbstractHelmMojo {


  /*
   * Instance fields.
   */


  /**
   * Whether to skip execution.
   */
  @Parameter(defaultValue = "false", property = "helm.skip")
  private boolean skip;

  /**
   * The {@code
   * Config} describing how a {@code
   * DefaultKubernetesClient} should connect to a Kubernetes
   * cluster.
   */
  @Parameter
  private Config clientConfiguration;

  /**
   * The Kubernetes cluster namespace in which Tiller may be found.
   */
  @Parameter(defaultValue = "kube-system", property = "tiller.namespace")
  private String tillerNamespace;
  
  /**
   * The port on which Tiller may be reached.
   */
  @Parameter(defaultValue = "44134", property = "tiller.port")
  private int tillerPort;

  /**
   * The Kubernetes labels normally found on Tiller pods.
   */
  @Parameter(property = "tiller.labels")
  private Map tillerLabels;

  
  /*
   * Constructors.
   */


  /**
   * Creates a new {@link AbstractReleaseMojo}.
   */
  protected AbstractReleaseMojo() {
    super();
  }


  /*
   * Public instance methods.
   */


  /**
   * {@linkplain #getSkip() Skips} execution if instructed, or calls
   * the {@link #execute(Callable)} method with a {@link Callable}
   * containing the results of an invocation of the {@link
   * #createReleaseManager(Tiller)} method.
   *
   * @exception MojoExecutionException if there was a problem
   * executing this mojo
   *
   * @exception MojoFailureException if the mojo executed properly,
   * but the job it was to perform failed in some way
   */
  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    final Log log = this.getLog();
    assert log != null;

    if (this.getSkip()) {
      if (log.isDebugEnabled()) {
        log.debug("Skipping execution by request.");
      }
      return;
    }

    final ReleaseManagerCallable releaseManagerCallable = new ReleaseManagerCallable();
    Throwable throwable = null;
    try {
      this.execute(releaseManagerCallable);
    } catch (final InterruptedException interruptedException) {
      Thread.currentThread().interrupt();
      final MojoExecutionException mojoExecutionException = new MojoExecutionException(interruptedException.getMessage(), interruptedException);
      throwable = mojoExecutionException;
      throw mojoExecutionException;
    } catch (final RuntimeException | MojoExecutionException | MojoFailureException throwMe) {
      throwable = throwMe;
      throw throwMe;
    } catch (final Exception otherStuff) {
      final MojoExecutionException mojoExecutionException = new MojoExecutionException(otherStuff.getMessage(), otherStuff);
      throwable = mojoExecutionException;
      throw mojoExecutionException;
    } finally {
      if (releaseManagerCallable.releaseManager != null) {
        try {
          releaseManagerCallable.releaseManager.close();
        } catch (final IOException ioException) {
          if (throwable != null) {
            throwable.addSuppressed(ioException);
          } else {
            throw new MojoExecutionException(ioException.getMessage(), ioException);
          }
        }
      }
    }
  }

  /**
   * Returns the {@link Config} describing how a {@link
   * DefaultKubernetesClient} is to connect to a Kubernetes cluster.
   *
   * 

This method may return {@code null}.

* *

Overrides of this method may return {@code null}.

* * @return a {@link Config}, or {@code null} * * @see #setClientConfiguration(Config) */ public Config getClientConfiguration() { return this.clientConfiguration; } /** * Installs the {@link Config} describing how a {@link * DefaultKubernetesClient} is to connect to a Kubernetes cluster. * * @param config the {@link Config} to use; may be {@code null} * * @see #getClientConfiguration() */ public void setClientConfiguration(final Config config) { this.clientConfiguration = config; } /** * Returns {@code true} if this {@link AbstractReleaseMojo} should * not execute. * * @return {@code true} if this {@link AbstractReleaseMojo} should * not execute; {@code false} otherwise * * @see #setSkip(boolean) */ public boolean getSkip() { return this.skip; } /** * Controls whether this {@link AbstractReleaseMojo} should execute. * * @param skip if {@code true}, this {@link AbstractReleaseMojo} * will not execute * * @see #getSkip() */ public void setSkip(final boolean skip) { this.skip = skip; } /** * Returns the Kubernetes namespace in which Tiller may be found. * *

This method may return {@code null}.

* * @return the Kubernetes namespace in which Tiller may be found, or * {@code null} */ public String getTillerNamespace() { return this.tillerNamespace; } /** * Sets the Kubernetes namespace in which Tiller may be found. * * @param tillerNamespace the Kubernetes namespace in which Tiller * may be found; may be {@code null} in which case {@code * kube-system} will be used by the {@link * #createTiller(DefaultKubernetesClient)} method instead */ public void setTillerNamespace(String tillerNamespace) { this.tillerNamespace = tillerNamespace; } /** * Returns the port on which Tiller may be found. * * @return the port on which Tiller may be found; normally {@code * 44134} */ public int getTillerPort() { return this.tillerPort; } /** * Sets the port on which Tiller may be found. * * @param tillerPort the port on which Tiller may be found; normally * {@code 44134} */ public void setTillerPort(final int tillerPort) { this.tillerPort = tillerPort; } /** * Returns the Kubernetes labels that Tiller Pods have. * *

This method may return {@code null}.

* * @return the Kubernetes labels that Tiller Pods have, or {@code * null} */ public Map getTillerLabels() { return this.tillerLabels; } /** * Sets the Kubernetes labels that Tiller Pods have. * *

Tiller Pods are normally labeled with {@code app = helm} and * {@code name = tiller}.

* * @param tillerLabels a {@link Map} containing the labels; may be * {@code null} */ public void setTillerLabels(final Map tillerLabels) { this.tillerLabels = tillerLabels; } /* * Protected instance methods. */ /** * Performs a release-oriented task using a {@link ReleaseManager} * {@linkplain Callable#call() available} from the supplied {@link * Callable}. * * @param releaseManagerCallable the {@link Callable} that will * provide a {@link ReleaseManager}; must not be {@code null} * * @exception Exception if an error occurs */ protected abstract void execute(final Callable releaseManagerCallable) throws Exception; /** * Creates a {@link DefaultKubernetesClient} for communicating with * Kubernetes clusters. * *

This method never returns {@code null}.

* *

Overrides of this method must not return {@code null}.

* *

The default implementation calls the {@link * #getClientConfiguration()} method and {@linkplain * DefaultKubernetesClient#DefaultKubernetesClient(Config) uses its * return value}, unless it is {@code null}, in which case a new * {@link DefaultKubernetesClient} is created via its {@linkplain * DefaultKubernetesClient#DefaultKubernetesClient() no-argument * constructor}.

* * @return a new, non-{@code null} {@link DefaultKubernetesClient} * * @exception IOException if there was a problem creating the client */ protected DefaultKubernetesClient createClient() throws IOException { final DefaultKubernetesClient client; final Config config = this.getClientConfiguration(); if (config == null) { client = new DefaultKubernetesClient(); } else { client = new DefaultKubernetesClient(config); } return client; } /** * Creates a {@link Tiller} and returns it. * *

This method never returns {@code null}.

* *

Overrides of this method must not return {@code null}.

* *

This implementation passes the supplied {@link * DefaultKubernetesClient} to the appropriate * Tiller constructor.

* * @param client the {@link DefaultKubernetesClient} to use to * communicate with a Kubernetes cluster; must not be {@code null} * * @return a new {@link Tiller}; never {@code null} * * @exception NullPointerException if {@code client} is {@code null} * * @exception IOException if there was a problem creating a {@link * Tiller} */ protected Tiller createTiller(final DefaultKubernetesClient client) throws IOException { Objects.requireNonNull(client); return new Tiller(client, this.getTillerNamespace(), this.getTillerPort(), this.getTillerLabels()); } /** * Creates a {@link ReleaseManager} and returns it. * *

This method never returns {@code null}.

* *

Overrides of this method must not return {@code null}.

* *

This implementation passes the supplied {@link * Tiller} to the {@linkplain ReleaseManager#ReleaseManager(Tiller) * appropriate ReleaseManager constructor}.

* * @param tiller the {@link Tiller} to use to communicate with a * Tiller server; must not be {@code null} * * @return a new {@link ReleaseManager}; never {@code null} * * @exception NullPointerException if {@code tiller} is {@code null} * * @exception IOException if there was a problem creating a {@link * ReleaseManager} */ protected ReleaseManager createReleaseManager(final Tiller tiller) throws IOException { Objects.requireNonNull(tiller); return new ReleaseManager(tiller); } /** * Validates a Kubernetes * namespace for correctness. * *

The default implementation checks the supplied {@code * namespace} to see if it is less than or equal to {@value * ReleaseManager#DNS_LABEL_MAX_LENGTH} characters, and if it * {@linkplain Matcher#matches() matches} the value of the {@link * ReleaseManager#DNS_LABEL_PATTERN} field.

* * @param namespace the namespace * to validate; may be {@code null} * * @exception IllegalArgumentException if {@code namespace} is * invalid */ protected void validateNamespace(final String namespace) { if (namespace != null) { final int namespaceLength = namespace.length(); if (namespaceLength > ReleaseManager.DNS_LABEL_MAX_LENGTH) { throw new IllegalArgumentException("Invalid namespace: " + namespace + "; length is greater than " + ReleaseManager.DNS_LABEL_MAX_LENGTH + " characters: " + namespaceLength); } else if (namespaceLength > 0) { final Matcher matcher = ReleaseManager.DNS_LABEL_PATTERN.matcher(namespace); assert matcher != null; if (!matcher.matches()) { throw new IllegalArgumentException("Invalid namespace: " + namespace + "; must match " + ReleaseManager.DNS_LABEL_PATTERN.toString()); } } } } /* * Inner and nested classes. */ /** * A {@link Callable} whose {@link #call()} method yields the same * {@link ReleaseManager} for every invocation, {@linkplain * AbstractReleaseMojo#createReleaseManager(Tiller) creating one} if * necessary. * * @author Laird Nelson * * @see AbstractReleaseMojo#createReleaseManager(Tiller) * * @see AbstractReleaseMojo#createTiller(DefaultKubernetesClient) * * @see AbstractReleaseMojo#createClient() */ private final class ReleaseManagerCallable implements Callable { /* * Instance fields. */ /** * The {@link ReleaseManager} to return from the {@link #call()} * method. * *

This field may be {@code null}.

* * @sed #call() */ private ReleaseManager releaseManager; /* * Constructors. */ /** * Creates a new {@link ReleaseManagerCallable}. */ private ReleaseManagerCallable() { super(); } /* * Instance methods. */ /** * Returns a {@link ReleaseManager}, {@linkplain * AbstractReleaseMojo#createReleaseManager(Tiller) creating one} * if necessary. * *

This method never returns {@code null}.

* * @return a {@link ReleaseManager}; never {@code null} * * @exception IOException if there was a problem creating a {@link * ReleaseManager} */ @Override public final ReleaseManager call() throws IOException { if (this.releaseManager == null) { this.releaseManager = createReleaseManager(createTiller(createClient())); } return this.releaseManager; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy