Skip to content

Commit

Permalink
JBIDE-22192 - Add support for private registry authentication
Browse files Browse the repository at this point in the history
Added form fields to trigger a Docker image push upon wizard
completion
Added a button + wizard to look-up an image in the selected Docker's cache
instead of triggering a search on Docker Hub (or another registry)
Added a job to publish the selected Docker image into OpenShift registry
before deploying the image
Running the jobs in background to avoid blocking the UI, using a job
listener to collect the usage metrics.

When the image is pushed, the image name in the imagestream is prefixed with
 the project name

Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Fred Bricon <fbricon@gmail.com>
  • Loading branch information
xcoulon authored and fbricon committed Jun 2, 2016
1 parent 7c57ec4 commit 189fd3d
Show file tree
Hide file tree
Showing 14 changed files with 1,079 additions and 45 deletions.
3 changes: 2 additions & 1 deletion plugins/org.jboss.tools.openshift.ui/META-INF/MANIFEST.MF
Expand Up @@ -50,7 +50,8 @@ Require-Bundle: org.jboss.tools.openshift.common.ui;bundle-version="[3.0.0,4.0.0
org.eclipse.ui.editors,
org.eclipse.core.expressions;bundle-version="3.5.0",
org.eclipse.jst.server.core,
org.eclipse.swt;bundle-version="3.104.1"
org.eclipse.swt;bundle-version="3.104.1",
com.spotify.docker.client;bundle-version="3.4.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Export-Package: org.jboss.tools.openshift.internal.ui;x-friends:="org.jboss.tools.openshift.test",
Expand Down
@@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright (c) 2016 Red Hat.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat - Initial Contribution
*******************************************************************************/

package org.jboss.tools.openshift.internal.ui.dockerutils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerImage;

/**
* Utility class for {@link IDockerImage}s
*/
public class DockerImageUtils {

/**
* Checks if an image with the given {@code repo} and {@code tag} exists in
* the given {@code dockerConnection}
* <p>
* Workaround until https://bugs.eclipse.org/bugs/show_bug.cgi?id=495243 is
* fixed.
* </p>
*
* @param dockerConnection
* the {@link IDockerConnection}
* @param repoName
* the repository/name of the image to look-up
* @param tag
* the image tag
* @return <code>true</code> if match found, <code>false</code> otherwise
*/
public static boolean hasImage(final IDockerConnection dockerConnection, final String repoName, final String tag) {
for (IDockerImage image : dockerConnection.getImages()) {
final Map<String, List<String>> repoTags = extractTagsByRepo(image.repoTags());
for (Entry<String, List<String>> entry : repoTags.entrySet()) {
final String repo = entry.getKey();
final List<String> tags = entry.getValue();
if (repo != null && repo.equals(repoName) && tags != null && tags.contains(tag)) {
return true;
}
}
}
return false;
}

/**
* Extracts the org/repo and all the associated tags from the given
* {@code repoTags}, assuming that the given repoTags elements have the
* following format: {@code [org/]repo[:tag]}. Tags are sorted by their
* natural order.
*
* @param repoTags
* the list of repo/tags to analyze
* @return the tags indexed by org/repo
*/
public static Map<String, List<String>> extractTagsByRepo(final List<String> repoTags) {
final Map<String, List<String>> results = new HashMap<>();
for (String entry : repoTags) {
final int indexOfColonChar = entry.lastIndexOf(':');
final String repo = (indexOfColonChar > -1) ? entry.substring(0, indexOfColonChar) : entry;
if (!results.containsKey(repo)) {
results.put(repo, new ArrayList<String>());
}
if (indexOfColonChar > -1) {
results.get(repo).add(entry.substring(indexOfColonChar + 1));
}
}
// now sort the tags
for (Entry<String, List<String>> entry : results.entrySet()) {
Collections.sort(entry.getValue());
}
return results;
}

/**
* Retrieves the {@code 'name:tag'} from {@code '[[registry/]repo/]name:tag'} format of the given {@code imageName}
* @param imageName
* @return the {@code 'name:tag'}
*/
public static String extractImageNameAndTag(final String imageName) {
final int lastIndexOfSlash = imageName.lastIndexOf('/');
if(lastIndexOfSlash == -1) {
return imageName;
}
return imageName.substring(lastIndexOfSlash + 1);
}
}
@@ -0,0 +1,74 @@
package org.jboss.tools.openshift.internal.ui.dockerutils;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;

// FIXME: temporary code duplication until https://bugs.eclipse.org/bugs/show_bug.cgi?id=495251 is done.
public class ProgressJob extends Job {

private int percentageDone = 0;
private int percentageChange = 0;

private Object lockObject = new Object();

private String jobName;

public ProgressJob(String name, String jobName) {
super(name);
this.jobName = jobName;
}

@Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask(jobName, 100);
boolean done = false;

while (!done) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
// if work percentage has changed...add new amount
int change = getPercentageChange();
if (change > 0) {
monitor.worked(change);
setPercentageChange(0);
}
// if we are 100% or more done, then we are done
if (percentageDone >= 100) {
done = true;
}
// otherwise, sleep and then loop again
try {
Thread.sleep(500);
} catch (InterruptedException e) {
done = true;
}
}
monitor.done();
return Status.OK_STATUS;
}

private int getPercentageChange() {
synchronized (lockObject) {
return percentageChange;
}
}

private void setPercentageChange(int percentChange) {
synchronized (lockObject) {
this.percentageChange = percentChange;
}
}

public void setPercentageDone(int percentage) {
synchronized (lockObject) {
if (percentage > percentageDone) {
percentageChange = percentage - percentageDone;
percentageDone = percentage;
}
}
}

}
@@ -0,0 +1,106 @@
/*******************************************************************************
* Copyright (c) 2016 Red Hat.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat - Initial Contribution
*******************************************************************************/

package org.jboss.tools.openshift.internal.ui.dockerutils;

import java.net.MalformedURLException;
import java.net.URL;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.linuxtools.docker.core.DockerException;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IRegistryAccount;
import org.eclipse.linuxtools.internal.docker.ui.views.ImagePushProgressHandler;
import org.jboss.tools.openshift.internal.common.core.job.AbstractDelegatingMonitorJob;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;

import com.spotify.docker.client.DockerCertificateException;

/**
* {@link Job} to push an image from a Docker daemon into the OpenShift registry
*/
public class PushImageToRegistryJob extends AbstractDelegatingMonitorJob {

private final IDockerConnection dockerConnection;

private final IRegistryAccount registryAccount;

private final String imageName;

private final String openshiftProject;

/**
* Constructor
* @param dockerConnection the Docker connection to use
* @param registryAccount the registry account to push the image into
* @param openshiftProject the name of the OpenShift project, because the image has to be into the same namespace
* @param imageName the name of the image
*/
public PushImageToRegistryJob(final IDockerConnection dockerConnection, final IRegistryAccount registryAccount, final String openshiftProject, final String imageName) {
super("Pushing Docker image to OpenShift registry...");
this.dockerConnection = dockerConnection;
this.registryAccount = registryAccount;
this.imageName = imageName;
this.openshiftProject = openshiftProject;
}

@Override
protected IStatus doRun(final IProgressMonitor monitor) {
monitor.beginTask("Pushing image to registry", 1);
final String tmpImageName = getPushToRegistryImageName();
try {
// first, we need to tag the image with the OpenShift target
// project
this.dockerConnection.tagImage(imageName, tmpImageName);
// then we can push that image with the new name
this.dockerConnection.pushImage(tmpImageName, registryAccount,
new ImagePushProgressHandler(this.dockerConnection, tmpImageName));
}
// FIXME: needs to catch DockerCertificateException until
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=495249 is fixed
catch (DockerException | InterruptedException | DockerCertificateException e) {
return new Status(IStatus.ERROR, OpenShiftUIActivator.PLUGIN_ID,
"Failed to push the selected Docker image into OpenShift registry", e);
} finally {
// we need to untag the image, even if the push operation failed
try {
this.dockerConnection.removeTag(tmpImageName);
} catch (DockerException | InterruptedException e) {
return new Status(IStatus.WARNING, OpenShiftUIActivator.PLUGIN_ID,
"Pushed the selected Docker image into OpenShift registry but failed to untag it afterwards",
e);
}
monitor.done();
}
// TODO Auto-generated method stub
return Status.OK_STATUS;
}

/**
* @return the name used to push to the registry.
*/
public String getPushToRegistryImageName() {
try {
final String registryHostname = new URL(this.registryAccount.getServerAddress()).getHost();
final String tmpImageName = registryHostname + '/' + this.openshiftProject + '/' + DockerImageUtils.extractImageNameAndTag(this.imageName);
return tmpImageName;
} catch (MalformedURLException e) {
OpenShiftUIActivator.getDefault().getLog().log(new Status(IStatus.ERROR, OpenShiftUIActivator.PLUGIN_ID,
"Failed to push the selected Docker image into OpenShift registry", e));
return null;
}

}

}
@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2016 Red Hat.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat - Initial Contribution
*******************************************************************************/

/**
*
*/
package org.jboss.tools.openshift.internal.ui.dockerutils;
Expand Up @@ -117,7 +117,7 @@ public void done(IJobChangeEvent event) {
@Override
public void run() {
DeployImageWizard wizard = new DeployImageWizard(image, connection, project, connected[0]);
WizardUtils.openWizardDialog(600, 1500, wizard, shell);
WizardUtils.openWizardDialog(500, 500, wizard, shell);
}
});
}
Expand Down
Expand Up @@ -33,6 +33,7 @@
import org.jboss.tools.openshift.internal.core.Trace;
import org.jboss.tools.openshift.internal.core.util.OpenShiftProjectUtils;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
import org.jboss.tools.openshift.internal.ui.dockerutils.DockerImageUtils;
import org.jboss.tools.openshift.internal.ui.wizard.common.EnvironmentVariable;
import org.jboss.tools.openshift.internal.ui.wizard.common.IResourceLabelsPageModel.Label;
import org.jboss.tools.openshift.internal.ui.wizard.deployimage.IDeployImageParameters;
Expand Down Expand Up @@ -138,7 +139,13 @@ private Collection<IResource> createResources(Connection connection, Collection
private Map<String, IResource> generateResources(final Connection connection, final String name) {
final IResourceFactory factory = connection.getResourceFactory();
final IProject project = parameters.getProject();
DockerImageURI sourceImage = new DockerImageURI(parameters.getImageName());
String imageName;
if (parameters.isPushImageToRegistry()) {
imageName = project.getNamespace() +"/" + DockerImageUtils.extractImageNameAndTag(parameters.getImageName());
} else {
imageName = parameters.getImageName();
}
DockerImageURI sourceImage = new DockerImageURI(imageName);

Map<String, IResource> resources = new HashMap<>(4);

Expand Down

0 comments on commit 189fd3d

Please sign in to comment.