| /* |
| * 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.base.Throwables.propagate; |
| 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.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.IOException; |
| 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.Closeables; |
| 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 { |
| try { |
| Closeables.close(clientApi, true); |
| } catch (IOException e) { |
| throw propagate(e); |
| } |
| } |
| } |
| |
| } |