/** * Copyright 2016 Yahoo Inc. * * 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 com.yahoo.pulsar.admin.cli; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import java.util.EnumSet; import org.mockito.ArgumentMatcher; import org.mockito.Matchers; import org.mockito.Mockito; import org.testng.annotations.Test; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.yahoo.pulsar.client.admin.Brokers; import com.yahoo.pulsar.client.admin.Clusters; import com.yahoo.pulsar.client.admin.Lookup; import com.yahoo.pulsar.client.admin.Namespaces; import com.yahoo.pulsar.client.admin.PersistentTopics; import com.yahoo.pulsar.client.admin.Properties; import com.yahoo.pulsar.client.admin.PulsarAdmin; import com.yahoo.pulsar.client.admin.ResourceQuotas; import com.yahoo.pulsar.common.policies.data.AuthAction; import com.yahoo.pulsar.common.policies.data.BacklogQuota; import com.yahoo.pulsar.common.policies.data.BacklogQuota.RetentionPolicy; import com.yahoo.pulsar.common.policies.data.ClusterData; import com.yahoo.pulsar.common.policies.data.PersistencePolicies; import com.yahoo.pulsar.common.policies.data.PropertyAdmin; import com.yahoo.pulsar.common.policies.data.ResourceQuota; import com.yahoo.pulsar.common.policies.data.RetentionPolicies; @Test public class PulsarAdminToolTest { @Test void brokers() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); Brokers mockBrokers = mock(Brokers.class); doReturn(mockBrokers).when(admin).brokers(); CmdBrokers brokers = new CmdBrokers(admin); brokers.run(split("list use")); verify(mockBrokers).getActiveBrokers("use"); brokers.run(split("get-all-dynamic-config")); verify(mockBrokers).getAllDynamicConfigurations(); brokers.run(split("list-dynamic-config")); verify(mockBrokers).getDynamicConfigurationNames(); brokers.run(split("update-dynamic-config --config brokerShutdownTimeoutMs --value 100")); verify(mockBrokers).updateDynamicConfiguration("brokerShutdownTimeoutMs", "100"); } @Test void getOwnedNamespaces() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); Brokers mockBrokers = mock(Brokers.class); doReturn(mockBrokers).when(admin).brokers(); CmdBrokers brokers = new CmdBrokers(admin); brokers.run(split("namespaces use --url http://my-service.url:4000")); verify(mockBrokers).getOwnedNamespaces("use", "http://my-service.url:4000"); } @Test void clusters() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); Clusters mockClusters = mock(Clusters.class); when(admin.clusters()).thenReturn(mockClusters); CmdClusters clusters = new CmdClusters(admin); clusters.run(split("list")); verify(mockClusters).getClusters(); clusters.run(split("get use")); verify(mockClusters).getCluster("use"); clusters.run(split("create use --url http://my-service.url:8080")); verify(mockClusters).createCluster("use", new ClusterData("http://my-service.url:8080", null)); clusters.run(split("update use --url http://my-service.url:8080")); verify(mockClusters).updateCluster("use", new ClusterData("http://my-service.url:8080", null)); clusters.run(split("delete use")); verify(mockClusters).deleteCluster("use"); // Re-create CmdClusters to avoid a issue. // See https://github.com/cbeust/jcommander/issues/271 clusters = new CmdClusters(admin); clusters.run( split("create my-cluster --url http://my-service.url:8080 --url-secure https://my-service.url:4443")); verify(mockClusters).createCluster("my-cluster", new ClusterData("http://my-service.url:8080", "https://my-service.url:4443")); clusters.run( split("update my-cluster --url http://my-service.url:8080 --url-secure https://my-service.url:4443")); verify(mockClusters).updateCluster("my-cluster", new ClusterData("http://my-service.url:8080", "https://my-service.url:4443")); clusters.run(split("delete my-cluster")); verify(mockClusters).deleteCluster("my-cluster"); } @Test void properties() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); Properties mockProperties = mock(Properties.class); when(admin.properties()).thenReturn(mockProperties); CmdProperties properties = new CmdProperties(admin); properties.run(split("list")); verify(mockProperties).getProperties(); PropertyAdmin propertyAdmin = new PropertyAdmin(Lists.newArrayList("role1", "role2"), Sets.newHashSet("use")); properties.run(split("create property --admin-roles role1,role2 --allowed-clusters use")); verify(mockProperties).createProperty("property", propertyAdmin); propertyAdmin = new PropertyAdmin(Lists.newArrayList("role1", "role2"), Sets.newHashSet("usw")); properties.run(split("update property --admin-roles role1,role2 --allowed-clusters usw")); verify(mockProperties).updateProperty("property", propertyAdmin); properties.run(split("get property")); verify(mockProperties).getPropertyAdmin("property"); properties.run(split("delete property")); verify(mockProperties).deleteProperty("property"); } @Test void namespaces() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); Namespaces mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); Lookup mockLookup = mock(Lookup.class); when(admin.lookups()).thenReturn(mockLookup); CmdNamespaces namespaces = new CmdNamespaces(admin); namespaces.run(split("list myprop")); verify(mockNamespaces).getNamespaces("myprop"); namespaces.run(split("list-cluster myprop/clust")); verify(mockNamespaces).getNamespaces("myprop", "clust"); namespaces.run(split("destinations myprop/clust/ns1")); verify(mockNamespaces).getDestinations("myprop/clust/ns1"); namespaces.run(split("policies myprop/clust/ns1")); verify(mockNamespaces).getPolicies("myprop/clust/ns1"); namespaces.run(split("create myprop/clust/ns1")); verify(mockNamespaces).createNamespace("myprop/clust/ns1"); namespaces.run(split("delete myprop/clust/ns1")); verify(mockNamespaces).deleteNamespace("myprop/clust/ns1"); namespaces.run(split("permissions myprop/clust/ns1")); verify(mockNamespaces).getPermissions("myprop/clust/ns1"); namespaces.run(split("grant-permission myprop/clust/ns1 --role role1 --actions produce,consume")); verify(mockNamespaces).grantPermissionOnNamespace("myprop/clust/ns1", "role1", EnumSet.of(AuthAction.produce, AuthAction.consume)); namespaces.run(split("revoke-permission myprop/clust/ns1 --role role1")); verify(mockNamespaces).revokePermissionsOnNamespace("myprop/clust/ns1", "role1"); namespaces.run(split("set-clusters myprop/clust/ns1 -c use,usw,usc")); verify(mockNamespaces).setNamespaceReplicationClusters("myprop/clust/ns1", Lists.newArrayList("use", "usw", "usc")); namespaces.run(split("get-clusters myprop/clust/ns1")); verify(mockNamespaces).getNamespaceReplicationClusters("myprop/clust/ns1"); namespaces.run(split("unload myprop/clust/ns1")); verify(mockNamespaces).unload("myprop/clust/ns1"); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("unload myprop/clust/ns1 -b 0x80000000_0xffffffff")); verify(mockNamespaces).unloadNamespaceBundle("myprop/clust/ns1", "0x80000000_0xffffffff"); namespaces.run(split("split-bundle myprop/clust/ns1 -b 0x00000000_0xffffffff")); verify(mockNamespaces).splitNamespaceBundle("myprop/clust/ns1", "0x00000000_0xffffffff"); namespaces.run(split("get-backlog-quotas myprop/clust/ns1")); verify(mockNamespaces).getBacklogQuotaMap("myprop/clust/ns1"); namespaces.run(split("set-backlog-quota myprop/clust/ns1 -p producer_request_hold -l 10")); verify(mockNamespaces).setBacklogQuota("myprop/clust/ns1", new BacklogQuota(10, RetentionPolicy.producer_request_hold)); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("set-backlog-quota myprop/clust/ns1 -p producer_exception -l 10K")); verify(mockNamespaces).setBacklogQuota("myprop/clust/ns1", new BacklogQuota(10 * 1024, RetentionPolicy.producer_exception)); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("set-backlog-quota myprop/clust/ns1 -p producer_exception -l 10M")); verify(mockNamespaces).setBacklogQuota("myprop/clust/ns1", new BacklogQuota(10 * 1024 * 1024, RetentionPolicy.producer_exception)); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("set-backlog-quota myprop/clust/ns1 -p producer_exception -l 10G")); verify(mockNamespaces).setBacklogQuota("myprop/clust/ns1", new BacklogQuota(10l * 1024 * 1024 * 1024, RetentionPolicy.producer_exception)); namespaces.run(split("set-persistence myprop/clust/ns1 -e 2 -w 1 -a 1 -r 100.0")); verify(mockNamespaces).setPersistence("myprop/clust/ns1", new PersistencePolicies(2, 1, 1, 100.0d)); namespaces.run(split("get-persistence myprop/clust/ns1")); verify(mockNamespaces).getPersistence("myprop/clust/ns1"); namespaces.run(split("set-message-ttl myprop/clust/ns1 -ttl 300")); verify(mockNamespaces).setNamespaceMessageTTL("myprop/clust/ns1", 300); namespaces.run(split("get-message-ttl myprop/clust/ns1")); verify(mockNamespaces).getNamespaceMessageTTL("myprop/clust/ns1"); namespaces.run(split("set-retention myprop/clust/ns1 -t 1h -s 1M")); verify(mockNamespaces).setRetention("myprop/clust/ns1", new RetentionPolicies(60, 1)); namespaces.run(split("get-retention myprop/clust/ns1")); verify(mockNamespaces).getRetention("myprop/clust/ns1"); namespaces.run(split("clear-backlog myprop/clust/ns1 -force")); verify(mockNamespaces).clearNamespaceBacklog("myprop/clust/ns1"); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("clear-backlog -b 0x80000000_0xffffffff myprop/clust/ns1 -force")); verify(mockNamespaces).clearNamespaceBundleBacklog("myprop/clust/ns1", "0x80000000_0xffffffff"); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("clear-backlog -s my-sub myprop/clust/ns1 -force")); verify(mockNamespaces).clearNamespaceBacklogForSubscription("myprop/clust/ns1", "my-sub"); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("clear-backlog -b 0x80000000_0xffffffff -s my-sub myprop/clust/ns1 -force")); verify(mockNamespaces).clearNamespaceBundleBacklogForSubscription("myprop/clust/ns1", "0x80000000_0xffffffff", "my-sub"); namespaces.run(split("unsubscribe -s my-sub myprop/clust/ns1")); verify(mockNamespaces).unsubscribeNamespace("myprop/clust/ns1", "my-sub"); mockNamespaces = mock(Namespaces.class); when(admin.namespaces()).thenReturn(mockNamespaces); namespaces = new CmdNamespaces(admin); namespaces.run(split("unsubscribe -b 0x80000000_0xffffffff -s my-sub myprop/clust/ns1")); verify(mockNamespaces).unsubscribeNamespaceBundle("myprop/clust/ns1", "0x80000000_0xffffffff", "my-sub"); } @Test void resourceQuotas() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); ResourceQuotas mockResourceQuotas = mock(ResourceQuotas.class); when(admin.resourceQuotas()).thenReturn(mockResourceQuotas); CmdResourceQuotas cmdResourceQuotas = new CmdResourceQuotas(admin); ResourceQuota quota = new ResourceQuota(); quota.setMsgRateIn(10); quota.setMsgRateOut(20); quota.setBandwidthIn(10000); quota.setBandwidthOut(20000); quota.setMemory(100); quota.setDynamic(false); cmdResourceQuotas.run(split("get")); verify(mockResourceQuotas).getDefaultResourceQuota(); cmdResourceQuotas.run(split("set -mi 10 -mo 20 -bi 10000 -bo 20000 -mem 100")); verify(mockResourceQuotas).setDefaultResourceQuota(quota); // reset mocks mockResourceQuotas = mock(ResourceQuotas.class); when(admin.resourceQuotas()).thenReturn(mockResourceQuotas); cmdResourceQuotas = new CmdResourceQuotas(admin); cmdResourceQuotas.run(split("get --namespace myprop/clust/ns1 --bundle 0x80000000_0xffffffff")); verify(mockResourceQuotas).getNamespaceBundleResourceQuota("myprop/clust/ns1", "0x80000000_0xffffffff"); cmdResourceQuotas.run(split( "set --namespace myprop/clust/ns1 --bundle 0x80000000_0xffffffff -mi 10 -mo 20 -bi 10000 -bo 20000 -mem 100")); verify(mockResourceQuotas).setNamespaceBundleResourceQuota("myprop/clust/ns1", "0x80000000_0xffffffff", quota); cmdResourceQuotas .run(split("reset-namespace-bundle-quota --namespace myprop/clust/ns1 --bundle 0x80000000_0xffffffff")); verify(mockResourceQuotas).resetNamespaceBundleResourceQuota("myprop/clust/ns1", "0x80000000_0xffffffff"); } @Test void persistentTopics() throws Exception { PulsarAdmin admin = Mockito.mock(PulsarAdmin.class); PersistentTopics mockTopics = mock(PersistentTopics.class); when(admin.persistentTopics()).thenReturn(mockTopics); CmdPersistentTopics topics = new CmdPersistentTopics(admin); topics.run(split("delete persistent://myprop/clust/ns1/ds1")); verify(mockTopics).delete("persistent://myprop/clust/ns1/ds1"); topics.run(split("list myprop/clust/ns1")); verify(mockTopics).getList("myprop/clust/ns1"); topics.run(split("subscriptions persistent://myprop/clust/ns1/ds1")); verify(mockTopics).getSubscriptions("persistent://myprop/clust/ns1/ds1"); topics.run(split("unsubscribe persistent://myprop/clust/ns1/ds1 -s sub1")); verify(mockTopics).deleteSubscription("persistent://myprop/clust/ns1/ds1", "sub1"); topics.run(split("stats persistent://myprop/clust/ns1/ds1")); verify(mockTopics).getStats("persistent://myprop/clust/ns1/ds1"); topics.run(split("stats-internal persistent://myprop/clust/ns1/ds1")); verify(mockTopics).getInternalStats("persistent://myprop/clust/ns1/ds1"); topics.run(split("info-internal persistent://myprop/clust/ns1/ds1")); verify(mockTopics).getInternalInfo("persistent://myprop/clust/ns1/ds1"); topics.run(split("partitioned-stats persistent://myprop/clust/ns1/ds1 --per-partition")); verify(mockTopics).getPartitionedStats("persistent://myprop/clust/ns1/ds1", true); topics.run(split("skip-all persistent://myprop/clust/ns1/ds1 -s sub1")); verify(mockTopics).skipAllMessages("persistent://myprop/clust/ns1/ds1", "sub1"); topics.run(split("skip persistent://myprop/clust/ns1/ds1 -s sub1 -n 100")); verify(mockTopics).skipMessages("persistent://myprop/clust/ns1/ds1", "sub1", 100); topics.run(split("expire-messages persistent://myprop/clust/ns1/ds1 -s sub1 -t 100")); verify(mockTopics).expireMessages("persistent://myprop/clust/ns1/ds1", "sub1", 100); topics.run(split("expire-messages-all-subscriptions persistent://myprop/clust/ns1/ds1 -t 100")); verify(mockTopics).expireMessagesForAllSubscriptions("persistent://myprop/clust/ns1/ds1", 100); topics.run(split("create-partitioned-topic persistent://myprop/clust/ns1/ds1 --partitions 32")); verify(mockTopics).createPartitionedTopic("persistent://myprop/clust/ns1/ds1", 32); topics.run(split("list-partitioned-topics myprop/clust/ns1")); verify(mockTopics).getPartitionedTopicList("myprop/clust/ns1"); topics.run(split("get-partitioned-topic-metadata persistent://myprop/clust/ns1/ds1")); verify(mockTopics).getPartitionedTopicMetadata("persistent://myprop/clust/ns1/ds1"); topics.run(split("delete-partitioned-topic persistent://myprop/clust/ns1/ds1")); verify(mockTopics).deletePartitionedTopic("persistent://myprop/clust/ns1/ds1"); topics.run(split("peek-messages persistent://myprop/clust/ns1/ds1 -s sub1 -n 3")); verify(mockTopics).peekMessages("persistent://myprop/clust/ns1/ds1", "sub1", 3); // argument matcher for the timestamp in reset cursor. Since we can't verify exact timestamp, we check for a // range of +/- 1 second of the expected timestamp class TimestampMatcher extends ArgumentMatcher<Long> { @Override public boolean matches(Object argument) { long timestamp = (Long) argument; long expectedTimestamp = System.currentTimeMillis() - (1 * 60 * 1000); if (timestamp < (expectedTimestamp + 1000) && timestamp > (expectedTimestamp - 1000)) { return true; } return false; } } topics.run(split("reset-cursor persistent://myprop/clust/ns1/ds1 -s sub1 -t 1m")); verify(mockTopics).resetCursor(Matchers.eq("persistent://myprop/clust/ns1/ds1"), Matchers.eq("sub1"), Matchers.longThat(new TimestampMatcher())); } @Test void tool() throws Exception { java.util.Properties properties = new java.util.Properties(); properties.setProperty("serviceUrl", "http://api.messaging.use.example.com:8080"); PulsarAdminTool tool = new PulsarAdminTool(properties); assertEquals(tool.run(new String[0]), false); assertEquals(tool.run(split("clusters list")), false); assertEquals(tool.run(split("invalid")), false); assertEquals(tool.run(split("--help")), false); } String[] split(String s) { return s.split(" "); } }