Issue 579: added isReadOnly to catalog so that you can tell whether or not you can add items to it

This commit is contained in:
Adrian Cole 2011-05-29 20:50:54 -07:00
parent b14626b4cf
commit f437b7a092
16 changed files with 289 additions and 90 deletions

View File

@ -24,6 +24,7 @@ import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
@ -36,6 +37,7 @@ import org.jclouds.vcloud.VCloudAsyncClient;
import org.jclouds.vcloud.VCloudClient;
import org.jclouds.vcloud.VCloudLoginAsyncClient;
import org.jclouds.vcloud.domain.CatalogItem;
import org.jclouds.vcloud.domain.ReferenceType;
import org.jclouds.vcloud.domain.VAppTemplate;
import org.jclouds.vcloud.domain.VCloudSession;
import org.jclouds.vcloud.functions.VAppTemplatesForCatalogItems;
@ -47,15 +49,14 @@ import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
/**
* Configures the VCloud authentication service connection, including logging
* and http transport.
* Configures the VCloud authentication service connection, including logging and http transport.
*
* @author Adrian Cole
*/
@RequiresHttp
@ConfiguresRestClient
public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A extends VCloudAsyncClient> extends
CommonVCloudRestClientModule<S, A> {
CommonVCloudRestClientModule<S, A> {
public BaseVCloudRestClientModule(Class<S> syncClientType, Class<A> asyncClientType) {
super(syncClientType, asyncClientType);
@ -66,12 +67,29 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
super(syncClientType, asyncClientType, delegateMap);
}
@Singleton
public static class VCloudWritableCatalog extends WriteableCatalog {
private final VCloudClient client;
@Inject
public VCloudWritableCatalog(VCloudClient client) {
super(client);
this.client = client;
}
@Override
public boolean apply(ReferenceType arg0) {
return !client.getCatalogClient().getCatalog(arg0.getHref()).isReadOnly();
}
}
@Override
protected void configure() {
bind(new TypeLiteral<Function<Iterable<? extends CatalogItem>, Iterable<? extends VAppTemplate>>>() {
}).to(new TypeLiteral<VAppTemplatesForCatalogItems>() {
});
bind(ResourceAllocationSettingDataHandler.class).to(VCloudResourceAllocationSettingDataHandler.class);
bind(WriteableCatalog.class).to(VCloudWritableCatalog.class);
super.configure();
}
@ -84,21 +102,21 @@ public abstract class BaseVCloudRestClientModule<S extends VCloudClient, A exten
@Provides
@Singleton
protected Supplier<VCloudSession> provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
final VCloudLoginAsyncClient login) {
final VCloudLoginAsyncClient login) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<VCloudSession>(authException, seconds,
new Supplier<VCloudSession>() {
new Supplier<VCloudSession>() {
@Override
public VCloudSession get() {
try {
return login.login().get(10, TimeUnit.SECONDS);
} catch (Exception e) {
propagate(e);
assert false : e;
return null;
@Override
public VCloudSession get() {
try {
return login.login().get(10, TimeUnit.SECONDS);
} catch (Exception e) {
propagate(e);
assert false : e;
return null;
}
}
}
});
});
}
}

View File

@ -109,7 +109,7 @@ public abstract class BaseVCloudAsyncClientTest<T> extends RestClientTest<T> {
}
@Override
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user) {
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user, WriteableCatalog writableCatalog) {
return URI.create("https://vcenterprise.bluelock.com/api/v1.0/catalog");
}
@ -255,7 +255,7 @@ public abstract class BaseVCloudAsyncClientTest<T> extends RestClientTest<T> {
"template", new ReferenceTypeImpl("template",
"application/vnd.vmware.vcloud.vAppTemplate+xml", URI
.create("https://vcenterprise.bluelock.com/api/v1.0/catalogItem/2"))),
ImmutableList.<Task> of(), true)));
ImmutableList.<Task> of(), true, false)));
}
}

View File

@ -18,7 +18,13 @@
*/
package org.jclouds.vcloud.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import org.jclouds.vcloud.BaseVCloudClientLiveTest;
import org.jclouds.vcloud.VCloudMediaType;
import org.jclouds.vcloud.domain.Org;
import org.jclouds.vcloud.domain.ReferenceType;
import org.testng.annotations.Test;
/**
@ -26,6 +32,17 @@ import org.testng.annotations.Test;
*/
@Test(groups = "live", enabled = true, singleThreaded = true, testName = "CatalogClientLiveTest")
public class CatalogClientLiveTest extends BaseVCloudClientLiveTest {
@Test
public void testGetCatalog() throws Exception {
Org org = getVCloudApi().getOrgClient().findOrgNamed(null);
for (ReferenceType catalog : org.getCatalogs().values()) {
assertEquals(catalog.getType(), VCloudMediaType.CATALOG_XML);
assertNotNull(getVCloudApi().getCatalogClient().getCatalog(catalog.getHref()));
}
}
@Test
public void testFindCatalogIsWriteable() throws Exception {
assertEquals(getVCloudApi().getCatalogClient().findCatalogInOrgNamed(null, null).isReadOnly(), false);
}
}

View File

@ -18,27 +18,16 @@
*/
package org.jclouds.vcloud.features;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.testng.Assert.assertNotNull;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.vcloud.BaseVCloudClientLiveTest;
import org.jclouds.vcloud.VCloudMediaType;
import org.jclouds.vcloud.domain.Org;
import org.jclouds.vcloud.domain.ReferenceType;
import org.jclouds.vcloud.domain.Task;
import org.jclouds.vcloud.domain.VApp;
import org.jclouds.vcloud.domain.VAppTemplate;
import org.jclouds.vcloud.domain.VDC;
import org.jclouds.vcloud.predicates.TaskSuccess;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
/**
*
* @author Adrian Cole
@ -63,48 +52,4 @@ public class VAppClientLiveTest extends BaseVCloudClientLiveTest {
}
}
}
@Test
public void testCaptureVApp() throws Exception {
String group = prefix + "cap";
NodeMetadata node = null;
VAppTemplate vappTemplate = null;
try {
node = getOnlyElement(client.createNodesInGroup(group, 1));
Predicate<URI> taskTester = new RetryablePredicate<URI>(new TaskSuccess(getVCloudApi()), 600, 5,
TimeUnit.SECONDS);
// I have to powerOff first
Task task = getVCloudApi().getVAppClient().powerOffVApp(URI.create(node.getId()));
// wait up to ten minutes per above
assert taskTester.apply(task.getHref()) : node;
// having a problem where the api is returning an error telling us to stop!
// // I have to undeploy first
// task = vcloudApi.undeployVAppOrVm(URI.create(node.getId()));
//
// // wait up to ten minutes per above
// assert taskTester.apply(task.getHref()) : node;
// vdc is equiv to the node's location
// vapp uri is the same as the node's id
vappTemplate = getVCloudApi().getVAppTemplateClient().captureVAppAsTemplateInVDC(URI.create(node.getId()),
group, URI.create(node.getLocation().getId()));
task = vappTemplate.getTasks().get(0);
// wait up to ten minutes per above
assert taskTester.apply(task.getHref()) : vappTemplate;
// TODO implement delete vAppTemplate
} finally {
if (node != null)
client.destroyNode(node.getId());
}
}
}

View File

@ -18,8 +18,15 @@
*/
package org.jclouds.vcloud.features;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.vcloud.BaseVCloudClientLiveTest;
import org.jclouds.vcloud.VCloudMediaType;
@ -27,8 +34,15 @@ import org.jclouds.vcloud.domain.Catalog;
import org.jclouds.vcloud.domain.CatalogItem;
import org.jclouds.vcloud.domain.Org;
import org.jclouds.vcloud.domain.ReferenceType;
import org.jclouds.vcloud.domain.Task;
import org.jclouds.vcloud.domain.VAppTemplate;
import org.jclouds.vcloud.options.CatalogItemOptions;
import org.jclouds.vcloud.predicates.TaskSuccess;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
/**
*
* @author Adrian Cole
@ -98,4 +112,64 @@ public class VAppTemplateClientLiveTest extends BaseVCloudClientLiveTest {
}
}
@Test
public void testCaptureVApp() throws Exception {
String group = prefix + "cap";
NodeMetadata node = null;
VAppTemplate vappTemplate = null;
CatalogItem item = null;
try {
node = getOnlyElement(client.createNodesInGroup(group, 1));
Predicate<URI> taskTester = new RetryablePredicate<URI>(new TaskSuccess(getVCloudApi()), 600, 5,
TimeUnit.SECONDS);
// I have to powerOff first
Task task = getVCloudApi().getVAppClient().powerOffVApp(URI.create(node.getId()));
// wait up to ten minutes per above
assert taskTester.apply(task.getHref()) : node;
// having a problem where the api is returning an error telling us to stop!
// I have to undeploy first
task = getVCloudApi().getVAppClient().undeployVApp(URI.create(node.getId()));
// wait up to ten minutes per above
assert taskTester.apply(task.getHref()) : node;
// vdc is equiv to the node's location
// vapp uri is the same as the node's id
vappTemplate = getVCloudApi().getVAppTemplateClient().captureVAppAsTemplateInVDC(URI.create(node.getId()),
group, URI.create(node.getLocation().getId()));
assertEquals(vappTemplate.getName(), group);
task = vappTemplate.getTasks().get(0);
// wait up to ten minutes per above
assert taskTester.apply(task.getHref()) : vappTemplate;
item = getVCloudApi().getCatalogClient().addVAppTemplateOrMediaImageToCatalogAndNameItem(
vappTemplate.getHref(),
getVCloudApi().getCatalogClient().findCatalogInOrgNamed(null, null).getHref(), "fooname",
CatalogItemOptions.Builder.description("description").properties(ImmutableMap.of("foo", "bar")));
assertEquals(item.getName(), "fooname");
assertEquals(item.getDescription(), "description");
assertEquals(item.getProperties(), ImmutableMap.of("foo", "bar"));
assertEquals(item.getEntity().getName(), vappTemplate.getName());
assertEquals(item.getEntity().getHref(), vappTemplate.getHref());
assertEquals(item.getEntity().getType(), vappTemplate.getType());
} finally {
if (item != null)
getVCloudApi().getCatalogClient().deleteCatalogItem(item.getHref());
if (vappTemplate != null)
getVCloudApi().getVAppTemplateClient().deleteVAppTemplate(vappTemplate.getHref());
if (node != null)
client.destroyNode(node.getId());
}
}
}

View File

@ -0,0 +1,103 @@
/**
*
* Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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.jclouds.vcloud.xml;
import static org.jclouds.vcloud.util.Utils.newReferenceType;
import static org.jclouds.vcloud.util.Utils.putReferenceType;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.SaxUtils;
import org.jclouds.vcloud.VCloudMediaType;
import org.jclouds.vcloud.domain.Catalog;
import org.jclouds.vcloud.domain.ReferenceType;
import org.jclouds.vcloud.domain.Task;
import org.jclouds.vcloud.domain.internal.CatalogImpl;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* @author Adrian Cole
*/
public class CatalogHandler extends ParseSax.HandlerWithResult<Catalog> {
protected final TaskHandler taskHandler;
@Inject
public CatalogHandler(TaskHandler taskHandler) {
this.taskHandler = taskHandler;
}
private StringBuilder currentText = new StringBuilder();
private ReferenceType catalog;
private Map<String, ReferenceType> contents = Maps.newLinkedHashMap();
protected List<Task> tasks = Lists.newArrayList();
private String description;
private ReferenceType org;
private boolean published = true;
public Catalog getResult() {
return new CatalogImpl(catalog.getName(), catalog.getType(), catalog.getHref(), org, description, contents,
tasks, published, false);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
Map<String, String> attributes = SaxUtils.cleanseAttributes(attrs);
if (qName.equals("Catalog")) {
catalog = newReferenceType(attributes, VCloudMediaType.CATALOG_XML);
} else if (qName.equals("CatalogItem")) {
putReferenceType(contents, attributes);
} else if (qName.equals("Link") && "up".equals(attributes.get("rel"))) {
org = newReferenceType(attributes);
} else {
taskHandler.startElement(uri, localName, qName, attrs);
}
}
public void endElement(String uri, String name, String qName) {
taskHandler.endElement(uri, name, qName);
if (qName.equals("Task")) {
this.tasks.add(taskHandler.getResult());
} else if (qName.equals("Description")) {
description = currentOrNull();
} else if (qName.equals("IsPublished")) {
published = Boolean.parseBoolean(currentOrNull());
}
currentText = new StringBuilder();
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
protected String currentOrNull() {
String returnVal = currentText.toString().trim();
return returnVal.equals("") ? null : returnVal;
}
}

View File

@ -679,9 +679,8 @@ public class VCloudExpressAsyncClientTest extends RestClientTest<VCloudExpressAs
}
@Override
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user) {
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user, WriteableCatalog write) {
return URI.create("https://vcloud.safesecureweb.com/api/v0.8/catalog");
}
@Override
@ -811,7 +810,7 @@ public class VCloudExpressAsyncClientTest extends RestClientTest<VCloudExpressAs
"template",
new ReferenceTypeImpl("template", "application/vnd.vmware.vcloud.vAppTemplate+xml", URI
.create("https://vcloud.safesecureweb.com/api/v0.8/catalogItem/2"))), ImmutableList
.<Task> of(), true)));
.<Task> of(), true, false)));
}
}

View File

@ -21,6 +21,7 @@ package org.jclouds.vcloud.config;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.transform;
@ -35,8 +36,9 @@ import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEO
import java.net.URI;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.SortedMap;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -195,9 +197,14 @@ public class CommonVCloudRestClientModule<S extends CommonVCloudClient, A extend
@Provides
@org.jclouds.vcloud.endpoints.Catalog
@Singleton
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user) {
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user, WriteableCatalog writableCatalog) {
checkState(org.getCatalogs().size() > 0, "No catalogs present in org: " + org.getName());
return get(org.getCatalogs().values(), 0).getHref();
try {
return find(org.getCatalogs().values(), writableCatalog).getHref();
} catch (NoSuchElementException e) {
throw new NoSuchElementException(String.format("no writable catalogs in org %s; catalogs %s", org, org
.getCatalogs()));
}
}
@Provides
@ -223,6 +230,21 @@ public class CommonVCloudRestClientModule<S extends CommonVCloudClient, A extend
"org"));
}
@Singleton
public static class WriteableCatalog implements Predicate<ReferenceType> {
private final CommonVCloudClient client;
@Inject
public WriteableCatalog(CommonVCloudClient client) {
this.client = client;
}
@Override
public boolean apply(ReferenceType arg0) {
return !client.getCatalog(arg0.getHref()).isReadOnly();
}
}
@Singleton
public static class OrgMapSupplier implements Supplier<Map<String, ? extends Org>> {
protected final Supplier<VCloudSession> sessionSupplier;

View File

@ -54,9 +54,14 @@ public interface Catalog extends ReferenceType, Map<String, ReferenceType> {
*
* @since vcloud api 1.0
*/
@Nullable
boolean isPublished();
/**
* @return true, if the current user cannot modify the catalog
* @since vcloud api 1.0
*/
boolean isReadOnly();
/**
* readonly container for Task elements. Each element in the container represents a queued,
* running, or failed task owned by this object.

View File

@ -52,9 +52,10 @@ public class CatalogImpl extends LinkedHashMap<String, ReferenceType> implements
private final String description;
private final List<Task> tasks = Lists.newArrayList();
private final boolean published;
private final boolean readOnly;
public CatalogImpl(String name, String type, URI href, ReferenceType org, @Nullable String description,
Map<String, ReferenceType> contents, Iterable<Task> tasks, boolean published) {
Map<String, ReferenceType> contents, Iterable<Task> tasks, boolean published, boolean readOnly) {
this.name = checkNotNull(name, "name");
this.type = checkNotNull(type, "type");
this.org = org;// TODO: once <1.0 is killed check not null
@ -63,6 +64,7 @@ public class CatalogImpl extends LinkedHashMap<String, ReferenceType> implements
putAll(checkNotNull(contents, "contents"));
Iterables.addAll(this.tasks, checkNotNull(tasks, "tasks"));
this.published = published;
this.readOnly = readOnly;
}
/**
@ -120,6 +122,14 @@ public class CatalogImpl extends LinkedHashMap<String, ReferenceType> implements
return published;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isReadOnly() {
return readOnly;
}
@Override
public int hashCode() {
final int prime = 31;

View File

@ -60,10 +60,11 @@ public class CatalogHandler extends ParseSax.HandlerWithResult<Catalog> {
private ReferenceType org;
private boolean published = true;
private boolean readOnly = true;
public Catalog getResult() {
return new CatalogImpl(catalog.getName(), catalog.getType(), catalog.getHref(), org, description, contents,
tasks, published);
tasks, published, readOnly);
}
@Override
@ -75,6 +76,8 @@ public class CatalogHandler extends ParseSax.HandlerWithResult<Catalog> {
putReferenceType(contents, attributes);
} else if (qName.equals("Link") && "up".equals(attributes.get("rel"))) {
org = newReferenceType(attributes);
} else if (qName.equals("Link") && "add".equals(attributes.get("rel"))) {
readOnly = false;
} else {
taskHandler.startElement(uri, localName, qName, attrs);
}

View File

@ -60,7 +60,7 @@ public class CatalogHandlerTest {
.create("https://vcenterprise.bluelock.com/api/v1.0/catalog/921222081"), new ReferenceTypeImpl(null,
"application/vnd.vmware.vcloud.org+xml", URI
.create("https://vcenterprise.bluelock.com/api/v1.0/org/9566014")), null, ImmutableMap
.<String, ReferenceType> of(), ImmutableList.<Task> of(), false));
.<String, ReferenceType> of(), ImmutableList.<Task> of(), false, false));
}
public void testTerremark() {

View File

@ -39,7 +39,7 @@ public class BlueLockVCloudDirectorComputeServiceLiveTest extends VCloudComputeS
public BlueLockVCloudDirectorComputeServiceLiveTest() {
provider = "bluelock-vcdirector";
// vcloud requires instantiate before deploy, which takes longer than 30 seconds
nonBlockDuration = 260;
nonBlockDuration = 300;
}
@Override

View File

@ -32,4 +32,8 @@ public class BlueLockVCloudDirectorVmClientLiveTest extends VmClientLiveTest {
public BlueLockVCloudDirectorVmClientLiveTest() {
provider = "bluelock-vcdirector";
}
protected void checkApiOutput(String apiOutput) {
checkApiOutput1_0_0(apiOutput);
}
}

View File

@ -48,9 +48,9 @@ import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.util.Strings2;
import org.jclouds.vcloud.CommonVCloudClient;
import org.jclouds.vcloud.VCloudVersionsAsyncClient;
import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogItemSupplier;
import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogSupplier;
import org.jclouds.vcloud.VCloudVersionsAsyncClient;
import org.jclouds.vcloud.config.CommonVCloudRestClientModule.OrgVDCSupplier;
import org.jclouds.vcloud.domain.AllocationModel;
import org.jclouds.vcloud.domain.Capacity;
@ -644,7 +644,7 @@ public class TerremarkECloudAsyncClientTest extends RestClientTest<TerremarkEClo
}
@Override
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user) {
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user, WriteableCatalog write) {
return URI.create("https://catalog");
}

View File

@ -48,9 +48,9 @@ import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.util.Strings2;
import org.jclouds.vcloud.CommonVCloudClient;
import org.jclouds.vcloud.VCloudVersionsAsyncClient;
import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogItemSupplier;
import org.jclouds.vcloud.VCloudExpressAsyncClientTest.VCloudRestClientModuleExtension.TestOrgCatalogSupplier;
import org.jclouds.vcloud.VCloudVersionsAsyncClient;
import org.jclouds.vcloud.domain.AllocationModel;
import org.jclouds.vcloud.domain.Capacity;
import org.jclouds.vcloud.domain.Catalog;
@ -661,9 +661,8 @@ public class TerremarkVCloudExpressAsyncClientTest extends RestClientTest<Terrem
}
@Override
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user) {
protected URI provideCatalog(Org org, @Named(PROPERTY_IDENTITY) String user, WriteableCatalog write) {
return URI.create("https://catalog");
}
@Override