Skip to content

Commit

Permalink
Fix #935: Implement k8s:push equivalent functionality k8sPush for…
Browse files Browse the repository at this point in the history
… Kubernetes Gradle Plugin

Kubernetes Push Task ported to gradle

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia authored and manusa committed Sep 29, 2021
1 parent ab72bed commit 39eabff
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 3 deletions.
23 changes: 23 additions & 0 deletions gradle-plugin/doc/src/main/asciidoc/inc/_groovy-configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ Defaults to `false`.
| `jkube.deploy.processTemplatesLocally`
endif::[]

| *pushRegistry*
| The registry to use when pushing the image. See <<registry,Registry Handling>> for
more details.
| `jkube.docker.push.registry`

| *recreate*
| Update resources by deleting them first and then creating them again.

Expand All @@ -282,6 +287,12 @@ Defaults to `false`.
| Specify globally a registry to use for pulling and pushing images. See <<registry,Registry handling>> for details.
| `jkube.docker.registry`

| *pushRetries*
| How often should a push be retried before giving up. This useful for flaky registries which tend to return 500 error codes from time to time.

Defaults to 0.
| `jkube.docker.push.retries`

| *resourceEnvironment*
| Environment name where resources are placed. For example, if you set this property to dev and resourceDir is the
default one, plugin will look at `src/main/jkube/dev`.
Expand Down Expand Up @@ -353,13 +364,25 @@ Defaults to `false`.
| Skip using docker machine in any case
| `jkube.docker.skip.machine`

| *skipPush*
| If set to true the plugin won’t push any images that have been built.

Defaults to `false`.
| `jkube.skip.push`

| *skipResourceValidation*
| If value is set to `true` then resource validation is skipped. This may be useful if resource validation is failing
for some reason but you still want to continue the deployment.

Default is `false`.
| `jkube.skipResourceValidation`

| *skipTag*
| If set to true this plugin won’t push any tags

Defaults to `false`.
| `jkube.skip.tag`

| *useProjectClassPath*
| Should we use the project's compile time classpath to scan for additional enrichers/generators.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[[jkube:push]]
=== *{task-prefix}Push*

This task uploads images to the registry which have a `build` configuration section. The images to push can be restricted with
the global option `filter` (see <<build-configuration,Build Goal Configuration>> for details). The registry to push is by
default `docker.io` but can be specified as part of the images's `name` the Docker way.
E.g. `docker.test.org:5000/data:1.5` will push the image `data` with tag `1.5` to the registry `docker.test.org` at port
`5000`. Registry credentials (i.e. username and password) can be specified in multiple ways as described in section <<authentication,Authentication>>.
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ include::_jkube-build.adoc[]

include::_jkube-resource.adoc[]

include::_jkube-apply.adoc[]
include::_jkube-apply.adoc[]

include::_jkube-push.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public void tasks_containsKubernetesAndOpenShiftTasks() {
.contains("k8sBuild - ")
.contains("k8sLog - ")
.contains("k8sResource - ")
.contains("k8sPush")
.contains("k8sUndeploy - ")
.contains("Openshift tasks")
.contains("ocApply - ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.jkube.gradle.plugin.task.KubernetesBuildTask;
import org.eclipse.jkube.gradle.plugin.task.KubernetesConfigViewTask;
import org.eclipse.jkube.gradle.plugin.task.KubernetesLogTask;
import org.eclipse.jkube.gradle.plugin.task.KubernetesPushTask;
import org.eclipse.jkube.gradle.plugin.task.KubernetesResourceTask;

import org.eclipse.jkube.gradle.plugin.task.KubernetesUndeployTask;
Expand All @@ -38,13 +39,15 @@ public KubernetesPlugin() {
public Map<String, Collection<Class<? extends Task>>> getTaskPrecedence() {
final Map<String, Collection<Class<? extends Task>>> ret = new HashMap<>();
ret.put("k8sApply", Collections.singletonList(KubernetesResourceTask.class));
ret.put("k8sPush", Collections.singletonList(KubernetesBuildTask.class));
return ret;
}

@Override
protected void jKubeApply(Project project) {
register(project, "k8sConfigView", KubernetesConfigViewTask.class);
register(project, "k8sBuild", KubernetesBuildTask.class);
register(project, "k8sPush", KubernetesPushTask.class);
register(project, "k8sResource", KubernetesResourceTask.class);
register(project, "k8sApply", KubernetesApplyTask.class);
register(project, "k8sLog", KubernetesLogTask.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2019 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at:
*
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.gradle.plugin.task;

import org.eclipse.jkube.gradle.plugin.KubernetesExtension;
import org.eclipse.jkube.kit.common.RegistryConfig;
import org.eclipse.jkube.kit.config.service.BuildServiceConfig;
import org.eclipse.jkube.kit.config.service.JKubeServiceException;
import org.eclipse.jkube.kit.config.service.JKubeServiceHub;

import javax.inject.Inject;
import java.util.Collections;

public class KubernetesPushTask extends AbstractJKubeTask {
@Inject
public KubernetesPushTask(Class<? extends KubernetesExtension> extensionClass) {
super(extensionClass);
setDescription("Uploads the built Docker images to a Docker registry");
}


@Override
protected JKubeServiceHub.JKubeServiceHubBuilder initJKubeServiceHubBuilder() {
return TaskUtil.addDockerServiceHubToJKubeServiceHubBuilder(super.initJKubeServiceHubBuilder(), kubernetesExtension, javaProject, kitLogger)
.buildServiceConfig(buildServiceConfigBuilder().build());
}

@Override
public void run() {
if (kubernetesExtension.getSkipPushOrDefault()) {
return;
}

try {
jKubeServiceHub.getBuildService().push(resolvedImages, kubernetesExtension.getPushRetriesOrDefault(), initRegistryConfig(kubernetesExtension.getPushRegistry().getOrNull()), kubernetesExtension.getSkipTagOrDefault());
} catch (JKubeServiceException e) {
throw new IllegalStateException("Error in pushing image: " + e.getMessage(), e);
}
}

private RegistryConfig initRegistryConfig(String specificRegistry) {
return RegistryConfig.builder()
.settings(Collections.emptyList())
.authConfig(kubernetesExtension.authConfig != null ? kubernetesExtension.authConfig.toMap() : null)
.skipExtendedAuth(kubernetesExtension.getSkipExtendedAuthOrDefault())
.registry(specificRegistry != null ? specificRegistry : kubernetesExtension.getRegistry().getOrNull())
.passwordDecryptionMethod(password -> password).build();
}

protected BuildServiceConfig.BuildServiceConfigBuilder buildServiceConfigBuilder() {
return TaskUtil.buildServiceConfigBuilder(kubernetesExtension, javaProject);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ public void getTaskPrecedence_withValidProject_shouldReturnTaskPrecedence() {
final Map<String, Collection<Class<? extends Task>>> result = new KubernetesPlugin().getTaskPrecedence();
// Then
assertThat(result)
.hasSize(1)
.containsEntry("k8sApply", Collections.singletonList(KubernetesResourceTask.class));
.hasSize(2)
.containsEntry("k8sApply", Collections.singletonList(KubernetesResourceTask.class))
.containsEntry("k8sPush", Collections.singletonList(KubernetesBuildTask.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2019 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at:
*
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.gradle.plugin.task;

import org.eclipse.jkube.gradle.plugin.KubernetesExtension;
import org.eclipse.jkube.gradle.plugin.TestKubernetesExtension;
import org.eclipse.jkube.kit.config.image.ImageConfiguration;
import org.eclipse.jkube.kit.config.image.build.BuildConfiguration;
import org.eclipse.jkube.kit.config.service.JKubeServiceException;
import org.eclipse.jkube.kit.config.service.kubernetes.DockerBuildService;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.JavaPluginConvention;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class KubernetesPushTaskTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

private MockedConstruction<DefaultTask> defaultTaskMockedConstruction;
private MockedConstruction<DockerBuildService> dockerBuildServiceMockedConstruction;
private List<ImageConfiguration> images;
private Project project;
private Logger logger;

@Before
public void setUp() throws IOException {
project = mock(Project.class, Mockito.RETURNS_DEEP_STUBS);
logger = mock(Logger.class);

defaultTaskMockedConstruction = mockConstruction(DefaultTask.class, (mock, ctx) -> {
when(mock.getProject()).thenReturn(project);
when(mock.getLogger()).thenReturn(logger);
});
dockerBuildServiceMockedConstruction = mockConstruction(DockerBuildService.class, (mock, ctx) -> {
when(mock.isApplicable()).thenReturn(true);
});
when(project.getConfigurations().stream()).thenAnswer(i -> Stream.empty());
when(project.getBuildscript().getConfigurations().stream()).thenAnswer(i -> Stream.empty());
when(project.getProjectDir()).thenReturn(temporaryFolder.getRoot());
when(project.getBuildDir()).thenReturn(temporaryFolder.newFolder("build"));
KubernetesExtension extension = new TestKubernetesExtension() {
@Override
public boolean isDockerAccessRequired() { return false; }
};
images = Collections.singletonList(ImageConfiguration.builder()
.name("foo/bar:latest")
.build(BuildConfiguration.builder()
.dockerFile("Dockerfile")
.build())
.build());
extension.images = images;
when(project.getExtensions().getByType(KubernetesExtension.class)).thenReturn(extension);
when(project.getConvention().getPlugin(JavaPluginConvention.class)).thenReturn(mock(JavaPluginConvention.class));
}

@Test
public void run_withImageConfiguration_shouldPushImage() throws JKubeServiceException {
// Given
final KubernetesPushTask kubernetesPushTask = new KubernetesPushTask(KubernetesExtension.class);

// When
kubernetesPushTask.runTask();

// Then
assertThat(dockerBuildServiceMockedConstruction.constructed()).hasSize(1);
verify(dockerBuildServiceMockedConstruction.constructed().iterator().next(), times(1))
.push(eq(images), eq(0), any(), eq(false));
}

@After
public void tearDown() {
defaultTaskMockedConstruction.close();
dockerBuildServiceMockedConstruction.close();
}
}

0 comments on commit 39eabff

Please sign in to comment.