blob: a477be0363c00c807608f7e53749c32cabf50c30 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.chef.internal;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.hash.Hashing.md5;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.jclouds.util.Closeables2.closeQuietly;
import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.jclouds.chef.ChefApi;
import org.jclouds.chef.domain.ChecksumStatus;
import org.jclouds.chef.domain.Client;
import org.jclouds.chef.domain.CookbookDefinition;
import org.jclouds.chef.domain.CookbookVersion;
import org.jclouds.chef.domain.DatabagItem;
import org.jclouds.chef.domain.Environment;
import org.jclouds.chef.domain.Metadata;
import org.jclouds.chef.domain.Node;
import org.jclouds.chef.domain.Resource;
import org.jclouds.chef.domain.Role;
import org.jclouds.chef.domain.Sandbox;
import org.jclouds.chef.domain.SearchResult;
import org.jclouds.chef.domain.UploadSandbox;
import org.jclouds.chef.options.CreateClientOptions;
import org.jclouds.chef.options.SearchOptions;
import org.jclouds.crypto.Pems;
import org.jclouds.io.ByteStreams2;
import org.jclouds.io.Payloads;
import org.jclouds.io.payloads.FilePayload;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.google.common.primitives.Bytes;
/**
* Tests behavior of {@code ChefApi}
*/
@Test(groups = { "live", "integration" })
public abstract class BaseChefApiLiveTest<A extends ChefApi> extends BaseChefLiveTest<A> {
public static final String PREFIX = "jcloudstest-" + System.getProperty("user.name");
public static final String ADMIN_PREFIX = "jcloudstest-adm-" + System.getProperty("user.name");
public static final String ENV_NODE = PREFIX + "-env-node";
// It may take a bit until the search index is populated
protected int maxWaitForIndexInMs = 60000;
// The id of the data bag item used in search tests
private String databagitemId;
public void testCreateNewCookbook() throws Exception {
// Define the file you want in the cookbook
File file = new File(System.getProperty("user.dir"), "pom.xml");
FilePayload content = Payloads.newFilePayload(file);
content.getContentMetadata().setContentType("application/x-binary");
// Get an md5 so that you can see if the server already has it or not
content.getContentMetadata().setContentMD5(Files.asByteSource(file).hash(Hashing.md5()).asBytes());
// Note that java collections cannot effectively do equals or hashcodes on
// byte arrays, so let's convert to a list of bytes.
List<Byte> md5 = Bytes.asList(content.getContentMetadata().getContentMD5());
// Request an upload site for this file
UploadSandbox site = api.createUploadSandboxForChecksums(ImmutableSet.of(md5));
assertTrue(site.getChecksums().containsKey(md5), md5 + " not in " + site.getChecksums());
try {
// Upload the file contents, if still not uploaded
ChecksumStatus status = site.getChecksums().get(md5);
if (status.needsUpload()) {
api.uploadContent(status.getUrl(), content);
}
Sandbox sandbox = api.commitSandbox(site.getSandboxId(), true);
assertTrue(sandbox.isCompleted(), "Sandbox should be completed after uploading");
} catch (RuntimeException e) {
api.commitSandbox(site.getSandboxId(), false);
fail("Could not upload content");
}
// Create the metadata of the cookbook
Metadata metadata = Metadata.builder() //
.name(PREFIX) //
.version("0.0.0") //
.description("Jclouds test uploaded cookbook") //
.maintainer("jclouds") //
.maintainerEmail("someone@jclouds.org") //
.license("Apache 2.0") //
.build();
// Create a new cookbook
CookbookVersion cookbook = CookbookVersion.builder(PREFIX, "0.0.0") //
.metadata(metadata) //
.rootFile(Resource.builder().fromPayload(content).build()) //
.build();
// upload the cookbook to the remote server
api.updateCookbook(PREFIX, "0.0.0", cookbook);
}
public void testListCookbooks() throws Exception {
Set<String> cookbookNames = api.listCookbooks();
assertFalse(cookbookNames.isEmpty(), "No cookbooks were found");
for (String cookbookName : cookbookNames) {
Set<String> versions = api.listVersionsOfCookbook(cookbookName);
assertFalse(versions.isEmpty(), "There are no versions of the cookbook: " + cookbookName);
for (String version : api.listVersionsOfCookbook(cookbookName)) {
CookbookVersion cookbook = api.getCookbook(cookbookName, version);
assertNotNull(cookbook, "Could not get cookbook: " + cookbookName);
}
}
}
@Test(dependsOnMethods = "testListCookbooks")
public void testListCookbookVersionsWithChefService() throws Exception {
Iterable<? extends CookbookVersion> cookbooks = chefService.listCookbookVersions();
assertFalse(isEmpty(cookbooks), "No cookbooks were found");
}
@Test(dependsOnMethods = "testListCookbookVersionsWithChefService")
public void testDownloadCookbooks() throws Exception {
Iterable<? extends CookbookVersion> cookbooks = chefService.listCookbookVersions();
for (CookbookVersion cookbook : cookbooks) {
for (Resource resource : ImmutableList.<Resource> builder().addAll(cookbook.getDefinitions())
.addAll(cookbook.getFiles()).addAll(cookbook.getLibraries()).addAll(cookbook.getSuppliers())
.addAll(cookbook.getRecipes()).addAll(cookbook.getResources()).addAll(cookbook.getRootFiles())
.addAll(cookbook.getTemplates()).build()) {
InputStream stream = api.getResourceContents(resource);
assertNotNull(stream, "Resource contents are null for resource: " + resource.getName());
byte[] md5 = ByteStreams2.hashAndClose(stream, md5()).asBytes();
assertEquals(md5, resource.getChecksum());
}
}
}
@Test(dependsOnMethods = "testCreateNewCookbook")
public void testUpdateCookbook() throws Exception {
CookbookVersion cookbook = api.getCookbook(PREFIX, "0.0.0");
assertNotNull(cookbook, "Cookbook not found: " + PREFIX);
assertNotNull(api.updateCookbook(PREFIX, "0.0.0", cookbook), "Updated cookbook was null");
}
@Test(dependsOnMethods = { "testCreateNewCookbook", "testUpdateCookbook" })
public void testDeleteCookbook() throws Exception {
assertNotNull(api.deleteCookbook(PREFIX, "0.0.0"), "Deleted cookbook was null");
}
@Test
public void testCreateClient() throws Exception {
api.deleteClient(PREFIX);
String credential = Pems.pem(api.createClient(PREFIX).getPrivateKey());
assertClientCreated(PREFIX, credential);
}
@Test
public void testCreateAdminClient() throws Exception {
api.deleteClient(ADMIN_PREFIX);
String credential = Pems.pem(api.createClient(ADMIN_PREFIX, CreateClientOptions.Builder.admin()).getPrivateKey());
assertClientCreated(ADMIN_PREFIX, credential);
}
@Test(dependsOnMethods = "testCreateClient")
public void testGenerateKeyForClient() throws Exception {
String credential = Pems.pem(api.generateKeyForClient(PREFIX).getPrivateKey());
assertClientCreated(PREFIX, credential);
}
@Test
public void testListNodes() throws Exception {
Set<String> nodes = api.listNodes();
assertNotNull(nodes, "No nodes were found");
}
@Test(dependsOnMethods = "testCreateRole")
public void testCreateNode() throws Exception {
api.deleteNode(PREFIX);
api.createNode(Node.builder().name(PREFIX).runListElement("role[" + PREFIX + "]").environment("_default").build());
Node node = api.getNode(PREFIX);
// TODO check recipes
assertNotNull(node, "Created node should not be null");
Set<String> nodes = api.listNodes();
assertTrue(nodes.contains(PREFIX), String.format("node %s not in %s", PREFIX, nodes));
}
@Test(dependsOnMethods = "testCreateNode")
public void testUpdateNode() throws Exception {
for (String nodename : api.listNodes()) {
Node node = api.getNode(nodename);
api.updateNode(node);
}
}
@Test
public void testListRoles() throws Exception {
Set<String> roles = api.listRoles();
assertNotNull(roles, "Role list was null");
}
@Test
public void testCreateRole() throws Exception {
api.deleteRole(PREFIX);
api.createRole(Role.builder().name(PREFIX).runListElement("recipe[java]").build());
Role role = api.getRole(PREFIX);
assertNotNull(role, "Created role should not be null");
assertEquals(role.getName(), PREFIX);
assertEquals(role.getRunList(), Collections.singleton("recipe[java]"));
}
@Test(dependsOnMethods = "testCreateRole")
public void testUpdateRole() throws Exception {
for (String rolename : api.listRoles()) {
Role role = api.getRole(rolename);
api.updateRole(role);
}
}
@Test
public void testListDatabags() throws Exception {
Set<String> databags = api.listDatabags();
assertNotNull(databags, "Data bag list was null");
}
@Test
public void testCreateDatabag() throws Exception {
api.deleteDatabag(PREFIX);
api.createDatabag(PREFIX);
}
@Test(dependsOnMethods = "testCreateDatabagItem")
public void testListDatabagItems() throws Exception {
Set<String> databagItems = api.listDatabagItems(PREFIX);
assertNotNull(databagItems, "Data bag item list was null");
}
@Test(dependsOnMethods = "testCreateDatabag")
public void testCreateDatabagItem() throws Exception {
Properties config = new Properties();
config.setProperty("foo", "bar");
api.deleteDatabagItem(PREFIX, PREFIX);
DatabagItem databagItem = api.createDatabagItem(PREFIX, new DatabagItem("config", json.toJson(config)));
databagitemId = databagItem.getId();
assertNotNull(databagItem, "Created data bag item should not be null");
assertEquals(databagItem.getId(), "config");
// The databagItem json contains extra keys: (the name and the type if the
// item)
Properties props = json.fromJson(databagItem.toString(), Properties.class);
for (Object key : config.keySet()) {
assertTrue(props.containsKey(key));
assertEquals(config.get(key), props.get(key));
}
}
@Test(dependsOnMethods = "testCreateDatabagItem")
public void testUpdateDatabagItem() throws Exception {
for (String databagItemId : api.listDatabagItems(PREFIX)) {
DatabagItem databagItem = api.getDatabagItem(PREFIX, databagItemId);
api.updateDatabagItem(PREFIX, databagItem);
}
}
@Test(dependsOnMethods = "testSearchDatabagWithOptions")
public void testDeleteDatabagItem() throws Exception {
for (String databagItemId : api.listDatabagItems(PREFIX)) {
DatabagItem databagItem = api.deleteDatabagItem(PREFIX, databagItemId);
assertNotNull(databagItem, "Deleted data bag item should not be null");
assertEquals(databagItem.getId(), databagItemId, "Deleted data bag item id must match the original id");
assertNull(api.getDatabagItem(PREFIX, databagItemId), "Data bag item should not exist");
}
}
@Test
public void testListSearchIndexes() throws Exception {
Set<String> indexes = api.listSearchIndexes();
assertNotNull(indexes, "The index list should not be null");
assertTrue(indexes.contains("node"));
assertTrue(indexes.contains("client"));
assertTrue(indexes.contains("role"));
}
@Test
public void testSearchNodes() throws Exception {
SearchResult<? extends Node> results = api.searchNodes();
assertNotNull(results, "Node result list should not be null");
}
@Test(dependsOnMethods = { "testListSearchIndexes", "testCreateNode" })
public void testSearchNodesWithOptions() throws Exception {
Predicate<SearchOptions> waitForIndex = retry(new Predicate<SearchOptions>() {
@Override
public boolean apply(SearchOptions input) {
SearchResult<? extends Node> results = api.searchNodes(input);
assertNotNull(results);
if (results.size() > 0) {
assertEquals(results.size(), 1);
assertEquals(results.iterator().next().getName(), PREFIX);
return true;
} else {
// The index may still not be populated
return false;
}
}
}, maxWaitForIndexInMs, 5000L, MILLISECONDS);
SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
assertTrue(waitForIndex.apply(options));
}
@Test
public void testSearchClients() throws Exception {
SearchResult<? extends Client> results = api.searchClients();
assertNotNull(results, "Client result list should not be null");
}
@Test(dependsOnMethods = { "testListSearchIndexes", "testCreateClient" })
public void testSearchClientsWithOptions() throws Exception {
Predicate<SearchOptions> waitForIndex = retry(new Predicate<SearchOptions>() {
@Override
public boolean apply(SearchOptions input) {
SearchResult<? extends Client> results = api.searchClients(input);
assertNotNull(results);
if (results.size() > 0) {
assertEquals(results.size(), 1);
assertEquals(results.iterator().next().getName(), PREFIX);
return true;
} else {
// The index may still not be populated
return false;
}
}
}, maxWaitForIndexInMs, 5000L, MILLISECONDS);
SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
assertTrue(waitForIndex.apply(options));
}
@Test
public void testSearchRoles() throws Exception {
SearchResult<? extends Role> results = api.searchRoles();
assertNotNull(results, "Role result list should not be null");
}
@Test(dependsOnMethods = { "testListSearchIndexes", "testCreateRole" })
public void testSearchRolesWithOptions() throws Exception {
Predicate<SearchOptions> waitForIndex = retry(new Predicate<SearchOptions>() {
@Override
public boolean apply(SearchOptions input) {
SearchResult<? extends Role> results = api.searchRoles(input);
assertNotNull(results);
if (results.size() > 0) {
assertEquals(results.size(), 1);
assertEquals(results.iterator().next().getName(), PREFIX);
return true;
} else {
// The index may still not be populated
return false;
}
}
}, maxWaitForIndexInMs, 5000L, MILLISECONDS);
SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
assertTrue(waitForIndex.apply(options));
}
@Test(dependsOnMethods = { "testListSearchIndexes", "testCreateDatabagItem" })
public void testSearchDatabag() throws Exception {
SearchResult<? extends DatabagItem> results = api.searchDatabagItems(PREFIX);
assertNotNull(results, "Data bag item result list should not be null");
}
@Test(dependsOnMethods = { "testListSearchIndexes", "testCreateDatabagItem" })
public void testSearchDatabagWithOptions() throws Exception {
Predicate<SearchOptions> waitForIndex = retry(new Predicate<SearchOptions>() {
@Override
public boolean apply(SearchOptions input) {
SearchResult<? extends DatabagItem> results = api.searchDatabagItems(PREFIX, input);
assertNotNull(results);
if (results.size() > 0) {
assertEquals(results.size(), 1);
assertEquals(results.iterator().next().getId(), databagitemId);
return true;
} else {
// The index may still not be populated
return false;
}
}
}, maxWaitForIndexInMs, 5000L, MILLISECONDS);
SearchOptions options = SearchOptions.Builder.query("id:" + databagitemId);
assertTrue(waitForIndex.apply(options));
}
@Test(expectedExceptions = ResourceNotFoundException.class, dependsOnMethods = "testListSearchIndexes")
public void testSearchDatabagNotFound() throws Exception {
SearchResult<? extends DatabagItem> results = api.searchDatabagItems("whoopie");
assertNotNull(results, "Data bag item result list should not be null");
}
@Test
public void testCreateEnvironment() {
api.deleteEnvironment(PREFIX);
api.createEnvironment(Environment.builder().name(PREFIX).description(PREFIX).build());
Environment env = api.getEnvironment(PREFIX);
assertNotNull(env, "Created environment should not be null");
assertEquals(env.getName(), PREFIX);
assertEquals(env.getDescription(), PREFIX);
}
@Test(dependsOnMethods = "testCreateEnvironment")
public void testListEnvironment() {
Set<String> envList = api.listEnvironments();
assertNotNull(envList, "Environment list was null");
assertTrue(envList.contains(PREFIX));
}
@Test(dependsOnMethods = "testCreateEnvironment")
public void testSearchEnvironments() throws Exception {
SearchResult<? extends Environment> results = api.searchEnvironments();
assertNotNull(results, "Environment result list was null");
}
@Test(dependsOnMethods = { "testListSearchIndexes", "testCreateEnvironment" })
public void testSearchEnvironmentsWithOptions() throws Exception {
Predicate<SearchOptions> waitForIndex = retry(new Predicate<SearchOptions>() {
@Override
public boolean apply(SearchOptions input) {
SearchResult<? extends Environment> results = api.searchEnvironments(input);
assertNotNull(results);
if (results.size() > 0) {
assertEquals(results.size(), 1);
assertEquals(results.iterator().next().getName(), PREFIX);
return true;
} else {
// The index may still not be populated
return false;
}
}
}, maxWaitForIndexInMs, 5000L, MILLISECONDS);
SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
assertTrue(waitForIndex.apply(options));
}
@Test(dependsOnMethods = "testCreateEnvironment")
public void testListRecipesInEnvironment() {
Set<String> recipeList = api.listRecipesInEnvironment(PREFIX);
assertTrue(!recipeList.isEmpty());
}
@Test(dependsOnMethods = "testCreateEnvironment")
public void testListNodesInEnvironment() {
api.deleteNode(ENV_NODE);
api.createNode(Node.builder().name(ENV_NODE).runListElement("role[" + PREFIX + "]").environment(PREFIX).build());
Node node = api.getNode(ENV_NODE);
assertNotNull(node, "Created node should not be null");
Set<String> nodeList = api.listNodesInEnvironment(PREFIX);
assertTrue(!nodeList.isEmpty());
}
@Test(dependsOnMethods = "testCreateNewCookbook")
public void testListCookbooksInEnvironment() throws Exception {
Set<CookbookDefinition> cookbooks = api.listCookbooksInEnvironment("_default");
assertTrue(any(cookbooks, new Predicate<CookbookDefinition>() {
@Override
public boolean apply(CookbookDefinition input) {
return PREFIX.equals(input.getName());
}}), String.format("Cookbook %s not in %s", PREFIX, cookbooks));
}
@AfterClass(groups = { "live", "integration" })
@Override
public void tearDown() {
api.deleteClient(PREFIX);
api.deleteClient(ADMIN_PREFIX);
api.deleteNode(PREFIX);
api.deleteNode(ENV_NODE);
api.deleteRole(PREFIX);
api.deleteDatabag(PREFIX);
api.deleteEnvironment(PREFIX);
super.tearDown();
}
private void assertClientCreated(String identity, String credential) {
Properties overrides = super.setupProperties();
overrides.setProperty(provider + ".identity", identity);
overrides.setProperty(provider + ".credential", credential);
A clientApi = create(overrides, setupModules());
try {
Client client = clientApi.getClient(identity);
assertNotNull(client, "Client not found: " + identity);
} finally {
closeQuietly(clientApi);
}
}
}