/*******************************************************************************
* Copyright (c) 2015 Red Hat, Inc. Distributed under license by Red Hat, Inc.
* All rights reserved. This program is 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, Inc.
******************************************************************************/
package com.openshift.internal.restclient;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import java.util.Random;
import org.jboss.dmr.ModelNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.openshift.internal.restclient.model.DeploymentConfig;
import com.openshift.internal.restclient.model.ModelNodeBuilder;
import com.openshift.internal.restclient.model.Pod;
import com.openshift.internal.restclient.model.ReplicationController;
import com.openshift.internal.restclient.model.properties.ResourcePropertyKeys;
import com.openshift.restclient.ClientBuilder;
import com.openshift.restclient.IClient;
import com.openshift.restclient.NotFoundException;
import com.openshift.restclient.ResourceKind;
import com.openshift.restclient.images.DockerImageURI;
import com.openshift.restclient.model.IDeploymentConfig;
import com.openshift.restclient.model.IPod;
import com.openshift.restclient.model.IProject;
import com.openshift.restclient.model.IReplicationController;
import com.openshift.restclient.model.IResource;
import com.openshift.restclient.model.deploy.DeploymentTriggerType;
import static org.junit.Assert.fail;
/**
* @author Jeff Cantrill
*/
public class IntegrationTestHelper implements ResourcePropertyKeys {
public static final long MILLISECONDS_PER_SECOND = 1000;
public static final long MILLISECONDS_PER_MIN = MILLISECONDS_PER_SECOND * 60;
private static final String KEY_DEFAULT_PROJECT = "default.project";
private static final String KEY_SERVER_URL = "serverURL";
private static final String KEY_PASSWORD = "default.clusteradmin.password";
private static final String KEY_USER = "default.clusteradmin.user";
private static final String KEY_OPENSHIFT_LOCATION = "default.openshift.location";
private static final String INTEGRATIONTEST_PROPERTIES = "/openshiftv3IntegrationTest.properties";
private static final Logger LOG = LoggerFactory.getLogger(IntegrationTestHelper.class);
private final Properties prop;
public IntegrationTestHelper() {
this.prop = loadProperties(INTEGRATIONTEST_PROPERTIES);
}
public IClient createClient(){
return new ClientBuilder(prop.getProperty(KEY_SERVER_URL)).build();
}
public IClient createClientForBasicAuth() {
IClient client = new ClientBuilder(prop.getProperty(KEY_SERVER_URL))
.withUserName(getDefaultClusterAdminUser())
.withPassword(getDefaultClusterAdminPassword())
.build();
return client;
}
public String getDefaultNamespace(){
return prop.getProperty(KEY_DEFAULT_PROJECT);
}
public String generateNamespace() {
return String.format("%s-%s",getDefaultNamespace(), new Random().nextInt(9999));
}
public IProject generateProject(IClient client) {
IResource request = client.getResourceFactory().stub(ResourceKind.PROJECT_REQUEST, generateNamespace());
return (IProject) client.create(request);
}
/**
* Stub a pod definition to the openshift/hello-openshift
* image for purposes of testing.
* @param client
* @param project
* @return a pod definition that needs to be further created using the client
*/
public static IPod stubPod(IClient client, IProject project) {
//cluster shouldnt allow us to create pods directly
ModelNode builder = new ModelNodeBuilder()
.set(ResourcePropertyKeys.KIND, ResourceKind.POD)
.set(ResourcePropertyKeys.METADATA_NAME, "hello-openshift")
.set(ResourcePropertyKeys.METADATA_NAMESPACE, project.getName())
.add("spec.containers", new ModelNodeBuilder()
.set(ResourcePropertyKeys.NAME, "hello-openshift")
.set("image", "openshift/hello-openshift")
.add("ports", new ModelNodeBuilder()
.set("containerPort", 8080)
.set("protocol", "TCP")
)
)
.build();
return new Pod(builder, client, new HashMap<>());
}
public static IDeploymentConfig stubDeploymentConfig(IClient client, IProject project) {
IDeploymentConfig dc = new ResourceFactory(client).create("v1", ResourceKind.DEPLOYMENT_CONFIG);
((DeploymentConfig)dc).setName("hello-openshift");
((DeploymentConfig)dc).setNamespace(project.getName());
dc.setReplicas(1);
dc.setReplicaSelector("foo","bar");
dc.addContainer(dc.getName(), new DockerImageURI("openshift/hello-openshift"), new HashSet<>(), Collections.emptyMap(), Collections.emptyList());
dc.addTrigger(DeploymentTriggerType.CONFIG_CHANGE);
return dc;
}
public static IReplicationController stubReplicationController(IClient client, IProject project) {
IReplicationController rc = new ResourceFactory(client).create("v1", ResourceKind.REPLICATION_CONTROLLER);
((ReplicationController)rc).setName("hello-openshift-rc");
((ReplicationController)rc).setNamespace(project.getName());
rc.setReplicas(1);
rc.setReplicaSelector("foo","bar");
rc.addContainer(rc.getName(), new DockerImageURI("openshift/hello-openshift"), new HashSet<>(), Collections.emptyMap(), Collections.emptyList());
return rc;
}
/**
* Loads the properties from the given {@code propertyFileName}, then
* overrides from the System properties if any was given (this is a convenient way to
* override the default settings and avoid conflicting with the properties file in git)
*
* @return the properties to use in the test
* @throws IOException
*/
private static Properties loadProperties(final String propertyFileName) {
final Properties properties = new Properties();
try {
properties.load(IntegrationTestHelper.class.getResourceAsStream(propertyFileName));
} catch (IOException e) {
e.printStackTrace();
fail("Failed to load properties from file " + INTEGRATIONTEST_PROPERTIES + ": " + e.getMessage());
}
overrideIfExists(properties, KEY_SERVER_URL);
overrideIfExists(properties, KEY_DEFAULT_PROJECT);
overrideIfExists(properties, KEY_OPENSHIFT_LOCATION);
overrideIfExists(properties, KEY_USER);
overrideIfExists(properties, KEY_PASSWORD);
return properties;
}
private static void overrideIfExists(final Properties properties, final String propertyName){
// then override with the VM arguments (if any)
final String propertyValue = System.getProperty(propertyName);
if (propertyValue != null) {
properties.setProperty(propertyName, propertyValue);
}
}
public String getOpenShiftLocation() {
return prop.getProperty(KEY_OPENSHIFT_LOCATION);
}
public String getDefaultClusterAdminUser() {
return prop.getProperty(KEY_USER);
}
public String getDefaultClusterAdminPassword() {
return prop.getProperty(KEY_PASSWORD);
}
public String getServerUrl() {
return prop.getProperty(KEY_SERVER_URL);
}
public static void cleanUpResource(IClient client, IResource resource) {
if(client == null || resource == null) {
LOG.debug("Skipping cleanup as client %s or resource %s is null", client, resource);
}
try {
Thread.sleep(1000);
LOG.debug(String.format("Deleting resource: %s", resource));
client.delete(resource);
} catch (Exception e) {
LOG.warn("Exception deleting", e);
}
}
/**
* Wait for the resource to exist for cases where the test is faster
* then the server in reconciling its existence;
* @param client
* @param kind
* @param namespace
* @param name
* @param maxWaitMillis
* @return The resource or null if the maxWaitMillis was exceeded or the resource doesnt exist
*/
public static IResource waitForResource(IClient client, String kind, String namespace, String name, long maxWaitMillis) {
return waitForResource(client, kind, namespace, name, maxWaitMillis, new ReadyConditional() {
@Override
public boolean isReady(IResource resource) {
return resource != null;
}
});
}
/**
* Wait for the resource to exist for cases where the test is faster
* then the server in reconciling its existence;
*
* @param client
* @param kind
* @param namespace
* @param name
* @param maxWaitMillis
* @param conditional
* @return
*/
public static IResource waitForResource(IClient client, String kind, String namespace, String name, long maxWaitMillis, ReadyConditional conditional) {
IResource resource = null;
final long timeout = System.currentTimeMillis() + maxWaitMillis;
do {
try {
resource = client.get(kind, name, namespace);
if(resource != null && conditional != null) {
if(conditional.isReady(resource)) {
return resource;
}
resource = null;
}
}catch(NotFoundException e) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
}
}while(resource == null && System.currentTimeMillis() <= timeout);
return resource;
}
/**
* Interface that can evaluate a resource to determine if its ready
* @author jeff.cantrill
*
*/
public static interface ReadyConditional {
/**
*
* @param resource
* @return true if the resource is 'ready'
*/
boolean isReady(IResource resource);
}
}