mirror of https://github.com/apache/cloudstack.git
resolve conflict with main
This commit is contained in:
commit
6bcfbc6dfc
|
|
@ -301,8 +301,9 @@ public class EventTypes {
|
|||
public static final String EVENT_REGISTER_CNI_CONFIG = "REGISTER.CNI.CONFIG";
|
||||
public static final String EVENT_DELETE_CNI_CONFIG = "DELETE.CNI.CONFIG";
|
||||
|
||||
//register for user API and secret keys
|
||||
//user API and secret keys
|
||||
public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY";
|
||||
public static final String EVENT_DELETE_SECRET_API_KEY = "DELETE.USER.KEY";
|
||||
public static final String API_KEY_ACCESS_UPDATE = "API.KEY.ACCESS.UPDATE";
|
||||
|
||||
// Template Events
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm
|
|||
s_fsm.addTransition(State.Running, Event.ScaleDownRequested, State.Scaling);
|
||||
s_fsm.addTransition(State.Stopped, Event.ScaleUpRequested, State.ScalingStoppedCluster);
|
||||
s_fsm.addTransition(State.Scaling, Event.OperationSucceeded, State.Running);
|
||||
s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Alert);
|
||||
s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Running);
|
||||
s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationSucceeded, State.Stopped);
|
||||
s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationFailed, State.Alert);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.cloud.kubernetes.cluster;
|
|||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -33,8 +34,10 @@ public interface KubernetesServiceHelper extends Adapter {
|
|||
ControlledEntity findByUuid(String uuid);
|
||||
ControlledEntity findByVmId(long vmId);
|
||||
void checkVmCanBeDestroyed(UserVm userVm);
|
||||
void checkVmAffinityGroupsCanBeUpdated(UserVm userVm);
|
||||
boolean isValidNodeType(String nodeType);
|
||||
Map<String, Long> getServiceOfferingNodeTypeMap(Map<String, Map<String, String>> serviceOfferingNodeTypeMap);
|
||||
Map<String, Long> getTemplateNodeTypeMap(Map<String, Map<String, String>> templateNodeTypeMap);
|
||||
Map<String, List<Long>> getAffinityGroupNodeTypeMap(Map<String, Map<String, String>> affinityGroupNodeTypeMap);
|
||||
void cleanupForAccount(Account account);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,16 +19,15 @@ package com.cloud.user;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeyCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
|
|
@ -37,7 +36,17 @@ import com.cloud.network.vpc.VpcOffering;
|
|||
import com.cloud.offering.DiskOffering;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.ListUserKeyRulesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.dns.DnsServer;
|
||||
|
||||
public interface AccountService {
|
||||
|
||||
|
|
@ -98,7 +107,7 @@ public interface AccountService {
|
|||
|
||||
void markUserRegistered(long userId);
|
||||
|
||||
public String[] createApiKeyAndSecretKey(RegisterUserKeyCmd cmd);
|
||||
ApiKeyPair createApiKeyAndSecretKey(RegisterUserKeysCmd cmd);
|
||||
|
||||
public String[] createApiKeyAndSecretKey(final long userId);
|
||||
|
||||
|
|
@ -128,6 +137,8 @@ public interface AccountService {
|
|||
|
||||
void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource);
|
||||
|
||||
void validateCallingUserHasAccessToDesiredUser(Long userId);
|
||||
|
||||
Long finalizeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly);
|
||||
|
||||
/**
|
||||
|
|
@ -137,9 +148,15 @@ public interface AccountService {
|
|||
*/
|
||||
UserAccount getUserAccountById(Long userId);
|
||||
|
||||
public Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd);
|
||||
Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd);
|
||||
|
||||
public Pair<Boolean, Map<String, String>> getKeys(Long userId);
|
||||
ListResponse<ApiKeyPairResponse> listKeys(ListUserKeysCmd cmd);
|
||||
|
||||
List<ApiKeyPairPermission> listKeyRules(ListUserKeyRulesCmd cmd);
|
||||
|
||||
void deleteApiKey(DeleteUserKeysCmd cmd);
|
||||
|
||||
void deleteApiKey(ApiKeyPair id);
|
||||
|
||||
/**
|
||||
* Lists user two-factor authentication provider plugins
|
||||
|
|
@ -154,4 +171,13 @@ public interface AccountService {
|
|||
*/
|
||||
UserTwoFactorAuthenticator getUserTwoFactorAuthenticationProvider(final Long domainId);
|
||||
|
||||
ApiKeyPair getLatestUserKeyPair(Long userId);
|
||||
|
||||
ApiKeyPair getKeyPairById(Long id);
|
||||
|
||||
ApiKeyPair getKeyPairByApiKey(String apiKey);
|
||||
|
||||
String getAccessingApiKey(BaseCmd cmd);
|
||||
|
||||
List<RolePermissionEntity> getAllKeypairPermissions(String apiKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// 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 com.cloud.user;
|
||||
|
||||
public enum ApiKeyPairState {
|
||||
ENABLED, REMOVED, EXPIRED
|
||||
}
|
||||
|
|
@ -65,14 +65,6 @@ public interface User extends OwnedBy, InternalIdentity {
|
|||
|
||||
public void setState(Account.State state);
|
||||
|
||||
public String getApiKey();
|
||||
|
||||
public void setApiKey(String apiKey);
|
||||
|
||||
public String getSecretKey();
|
||||
|
||||
public void setSecretKey(String secretKey);
|
||||
|
||||
public String getTimezone();
|
||||
|
||||
public void setTimezone(String timezone);
|
||||
|
|
|
|||
|
|
@ -39,10 +39,6 @@ public interface UserAccount extends InternalIdentity {
|
|||
|
||||
String getState();
|
||||
|
||||
String getApiKey();
|
||||
|
||||
String getSecretKey();
|
||||
|
||||
Date getCreated();
|
||||
|
||||
Date getRemoved();
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ public interface VmDetailConstants {
|
|||
String CKS_NODE_TYPE = "node";
|
||||
String OFFERING = "offering";
|
||||
String TEMPLATE = "template";
|
||||
String AFFINITY_GROUP = "affinitygroup";
|
||||
|
||||
// VMware to KVM VM migrations specific
|
||||
String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm";
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.cloud.exception.PermissionDeniedException;
|
|||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.component.Adapter;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -31,8 +32,8 @@ public interface APIChecker extends Adapter {
|
|||
// If true, apiChecker has checked the operation
|
||||
// If false, apiChecker is unable to handle the operation or not implemented
|
||||
// On exception, checkAccess failed don't allow
|
||||
boolean checkAccess(User user, String apiCommandName) throws PermissionDeniedException;
|
||||
boolean checkAccess(Account account, String apiCommandName) throws PermissionDeniedException;
|
||||
boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException;
|
||||
boolean checkAccess(Account account, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException;
|
||||
/**
|
||||
* Verifies if the account has permission for the given list of APIs and returns only the allowed ones.
|
||||
*
|
||||
|
|
@ -43,4 +44,5 @@ public interface APIChecker extends Adapter {
|
|||
*/
|
||||
List<String> getApisAllowedToUser(Role role, User user, List<String> apiNames) throws PermissionDeniedException;
|
||||
boolean isEnabled();
|
||||
List<RolePermissionEntity> getImplicitRolePermissions(RoleType roleType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import org.apache.cloudstack.api.Identity;
|
|||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
public interface RolePermissionEntity extends InternalIdentity, Identity {
|
||||
public enum Permission {
|
||||
enum Permission {
|
||||
ALLOW, DENY
|
||||
}
|
||||
Rule getRule();
|
||||
|
|
|
|||
|
|
@ -104,5 +104,26 @@ public interface RoleService {
|
|||
|
||||
List<RolePermission> findAllPermissionsBy(Long roleId);
|
||||
|
||||
List<RolePermissionEntity> findAllRolePermissionsEntityBy(Long roleId, boolean considerImplicitRules);
|
||||
|
||||
Permission getRolePermission(String permission);
|
||||
|
||||
int removeRolesIfNeeded(List<? extends Role> roles);
|
||||
|
||||
/**
|
||||
* Checks if the role of the caller account has compatible permissions of the specified role permissions.
|
||||
* For each permission of the {@param rolePermissionsToAccess}, the role of the caller needs to contain the same permission.
|
||||
*
|
||||
* @param rolePermissions the permissions of the caller role.
|
||||
* @param rolePermissionsToAccess the permissions for the role that the caller role wants to access.
|
||||
* @return True if the role can be accessed with the given permissions; false otherwise.
|
||||
*/
|
||||
boolean roleHasPermission(Map<String, RolePermissionEntity> rolePermissions, List<RolePermissionEntity> rolePermissionsToAccess);
|
||||
|
||||
/**
|
||||
* Given a list of role permissions, returns a {@link Map} containing the API name as the key and the {@link RolePermissionEntity} for the API as the value.
|
||||
*
|
||||
* @param rolePermissions Permissions for the role from role.
|
||||
*/
|
||||
Map<String, RolePermissionEntity> getRoleRulesAndPermissions(List<RolePermissionEntity> rolePermissions);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,16 +25,18 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
public final class Rule {
|
||||
private final String rule;
|
||||
private final Pattern matchingPattern;
|
||||
private final static Pattern ALLOWED_PATTERN = Pattern.compile("^[a-zA-Z0-9*]+$");
|
||||
|
||||
public Rule(final String rule) {
|
||||
validate(rule);
|
||||
this.rule = rule;
|
||||
matchingPattern = Pattern.compile(rule.toLowerCase().replace("*", "(\\w*\\*?)+"));
|
||||
}
|
||||
|
||||
public boolean matches(final String commandName) {
|
||||
return StringUtils.isNotEmpty(commandName)
|
||||
&& commandName.toLowerCase().matches(rule.toLowerCase().replace("*", "\\w*"));
|
||||
return StringUtils.isNotEmpty(commandName) &&
|
||||
matchingPattern.matcher(commandName.toLowerCase()).matches();
|
||||
}
|
||||
|
||||
public String getRuleString() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.apache.cloudstack.acl.apikeypair;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface ApiKeyPair extends ControlledEntity, InternalIdentity, Identity {
|
||||
Long getUserId();
|
||||
Date getStartDate();
|
||||
Date getEndDate();
|
||||
Date getCreated();
|
||||
String getDescription();
|
||||
String getApiKey();
|
||||
String getSecretKey();
|
||||
String getName();
|
||||
Date getRemoved();
|
||||
void setRemoved(Date date);
|
||||
void validateDate();
|
||||
boolean hasEndDatePassed();
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.apache.cloudstack.acl.apikeypair;
|
||||
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
|
||||
public interface ApiKeyPairPermission extends RolePermissionEntity {
|
||||
long getApiKeyPairId();
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// 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.apache.cloudstack.acl.apikeypair;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ApiKeyPairService {
|
||||
List<ApiKeyPairPermission> findAllPermissionsByKeyPairId(Long apiKeyPairId, Long roleId);
|
||||
|
||||
ApiKeyPair findByApiKey(String apiKey);
|
||||
|
||||
ApiKeyPair findById(Long id);
|
||||
}
|
||||
|
|
@ -66,5 +66,4 @@ public interface AffinityGroupService {
|
|||
|
||||
boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import java.util.List;
|
|||
|
||||
public class AffinityProcessorBase extends AdapterBase implements AffinityGroupProcessor {
|
||||
|
||||
public static final String AFFINITY_TYPE_HOST = "host affinity";
|
||||
public static final String AFFINITY_TYPE_HOST_ANTI = "host anti-affinity";
|
||||
|
||||
protected String _type;
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.apache.cloudstack.api;
|
|||
public class ApiConstants {
|
||||
public static final String ACCOUNT = "account";
|
||||
public static final String ACCOUNTS = "accounts";
|
||||
public static final String ACCOUNT_NAME = "accountname";
|
||||
public static final String ACCOUNT_TYPE = "accounttype";
|
||||
public static final String ACCOUNT_ID = "accountid";
|
||||
public static final String ACCOUNT_IDS = "accountids";
|
||||
|
|
@ -46,6 +47,7 @@ public class ApiConstants {
|
|||
public static final String AS_NUMBER_ID = "asnumberid";
|
||||
public static final String ASN_RANGE = "asnrange";
|
||||
public static final String ASN_RANGE_ID = "asnrangeid";
|
||||
public static final String API_KEY_FILTER = "apikeyfilter";
|
||||
public static final String ASYNC_BACKUP = "asyncbackup";
|
||||
public static final String AUTO_SELECT = "autoselect";
|
||||
public static final String USER_API_KEY = "userapikey";
|
||||
|
|
@ -356,6 +358,7 @@ public class ApiConstants {
|
|||
public static final String JOB_STATUS = "jobstatus";
|
||||
public static final String KEEPALIVE_ENABLED = "keepaliveenabled";
|
||||
public static final String KERNEL_VERSION = "kernelversion";
|
||||
public static final String KEYPAIR_ID = "keypairid";
|
||||
public static final String KEY = "key";
|
||||
public static final String LABEL = "label";
|
||||
public static final String LASTNAME = "lastname";
|
||||
|
|
@ -521,9 +524,9 @@ public class ApiConstants {
|
|||
public static final String SCHEDULE = "schedule";
|
||||
public static final String SCHEDULE_ID = "scheduleid";
|
||||
public static final String SCOPE = "scope";
|
||||
public static final String USER_SECRET_KEY = "usersecretkey";
|
||||
public static final String SEARCH_BASE = "searchbase";
|
||||
public static final String SECONDARY_IP = "secondaryip";
|
||||
public static final String SECRET_KEY = "secretkey";
|
||||
public static final String SECURITY_GROUP_IDS = "securitygroupids";
|
||||
public static final String SECURITY_GROUP_NAMES = "securitygroupnames";
|
||||
public static final String SECURITY_GROUP_NAME = "securitygroupname";
|
||||
|
|
@ -540,6 +543,7 @@ public class ApiConstants {
|
|||
public static final String SHOW_RESOURCE_ICON = "showicon";
|
||||
public static final String SHOW_INACTIVE = "showinactive";
|
||||
public static final String SHOW_UNIQUE = "showunique";
|
||||
public static final String SHOW_PERMISSIONS = "showpermissions";
|
||||
public static final String SIGNATURE = "signature";
|
||||
public static final String SIGNATURE_VERSION = "signatureversion";
|
||||
public static final String SINCE = "since";
|
||||
|
|
@ -623,7 +627,6 @@ public class ApiConstants {
|
|||
public static final String USERNAME = "username";
|
||||
public static final String USER_CONFIGURABLE = "userconfigurable";
|
||||
public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist";
|
||||
public static final String USER_SECRET_KEY = "usersecretkey";
|
||||
public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork";
|
||||
public static final String USE_VIRTUAL_ROUTER_IP_RESOLVER = "userouteripresolver";
|
||||
public static final String UPDATE_IN_SEQUENCE = "updateinsequence";
|
||||
|
|
@ -765,6 +768,7 @@ public class ApiConstants {
|
|||
public static final String ROLE_TYPE = "roletype";
|
||||
public static final String ROLE_NAME = "rolename";
|
||||
public static final String PERMISSION = "permission";
|
||||
public static final String PERMISSIONS = "permissions";
|
||||
public static final String RULE = "rule";
|
||||
public static final String RULES = "rules";
|
||||
public static final String RULE_ID = "ruleid";
|
||||
|
|
@ -1028,7 +1032,7 @@ public class ApiConstants {
|
|||
public static final String NSX_PROVIDER_PORT = "nsxproviderport";
|
||||
public static final String NSX_CONTROLLER_ID = "nsxcontrollerid";
|
||||
public static final String S3_ACCESS_KEY = "accesskey";
|
||||
public static final String S3_SECRET_KEY = "secretkey";
|
||||
public static final String SECRET_KEY = "secretkey";
|
||||
public static final String S3_END_POINT = "endpoint";
|
||||
public static final String S3_BUCKET_NAME = "bucket";
|
||||
public static final String S3_SIGNER = "s3signer";
|
||||
|
|
@ -1240,6 +1244,13 @@ public class ApiConstants {
|
|||
public static final String MAX_SIZE = "maxsize";
|
||||
public static final String NODE_TYPE_OFFERING_MAP = "nodeofferings";
|
||||
public static final String NODE_TYPE_TEMPLATE_MAP = "nodetemplates";
|
||||
public static final String NODE_TYPE_AFFINITY_GROUP_MAP = "nodeaffinitygroups";
|
||||
public static final String CONTROL_AFFINITY_GROUP_IDS = "controlaffinitygroupids";
|
||||
public static final String CONTROL_AFFINITY_GROUP_NAMES = "controlaffinitygroupnames";
|
||||
public static final String WORKER_AFFINITY_GROUP_IDS = "workeraffinitygroupids";
|
||||
public static final String WORKER_AFFINITY_GROUP_NAMES = "workeraffinitygroupnames";
|
||||
public static final String ETCD_AFFINITY_GROUP_IDS = "etcdaffinitygroupids";
|
||||
public static final String ETCD_AFFINITY_GROUP_NAMES = "etcdaffinitygroupnames";
|
||||
|
||||
public static final String BOOT_TYPE = "boottype";
|
||||
public static final String BOOT_MODE = "bootmode";
|
||||
|
|
|
|||
|
|
@ -49,5 +49,7 @@ public interface ApiServerService {
|
|||
|
||||
boolean resetPassword(UserAccount userAccount, String token, String password);
|
||||
|
||||
String getDomainId(Map<String, Object[]> params);
|
||||
|
||||
boolean isPostRequestsAndTimestampsEnforced();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public abstract class BaseAsyncCmd extends BaseCmd {
|
|||
public static final String migrationSyncObject = "migration";
|
||||
public static final String snapshotHostSyncObject = "snapshothost";
|
||||
public static final String gslbSyncObject = "globalserverloadbalancer";
|
||||
public static final String user = "user";
|
||||
|
||||
private Object job;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.cloud.bgp.BGPService;
|
|||
import org.apache.cloudstack.acl.ProjectRoleService;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairService;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||
import org.apache.cloudstack.alert.AlertService;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
|
|
@ -222,6 +223,8 @@ public abstract class BaseCmd {
|
|||
@Inject
|
||||
public Ipv6Service ipv6Service;
|
||||
@Inject
|
||||
public ApiKeyPairService apiKeyPairService;
|
||||
@Inject
|
||||
public VnfTemplateManager vnfTemplateManager;
|
||||
@Inject
|
||||
public BucketApiService _bucketService;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import java.util.Set;
|
|||
|
||||
import org.apache.cloudstack.api.response.ConsoleSessionResponse;
|
||||
import org.apache.cloudstack.consoleproxy.ConsoleSession;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.api.ApiConstants.HostDetails;
|
||||
|
|
@ -41,6 +43,7 @@ import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
|
|||
import org.apache.cloudstack.api.response.BackupOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.BackupRepositoryResponse;
|
||||
import org.apache.cloudstack.api.response.BackupScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.BaseRolePermissionResponse;
|
||||
import org.apache.cloudstack.api.response.BucketResponse;
|
||||
import org.apache.cloudstack.api.response.CapacityResponse;
|
||||
import org.apache.cloudstack.api.response.ClusterResponse;
|
||||
|
|
@ -77,6 +80,7 @@ import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse;
|
|||
import org.apache.cloudstack.api.response.IpForwardingRuleResponse;
|
||||
import org.apache.cloudstack.api.response.IpQuarantineResponse;
|
||||
import org.apache.cloudstack.api.response.IsolationMethodResponse;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.LBHealthCheckResponse;
|
||||
import org.apache.cloudstack.api.response.LBStickinessResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
|
|
@ -583,4 +587,8 @@ public interface ResponseGenerator {
|
|||
GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin);
|
||||
|
||||
ConsoleSessionResponse createConsoleSessionResponse(ConsoleSession consoleSession, ResponseView responseView);
|
||||
|
||||
ApiKeyPairResponse createKeyPairResponse(ApiKeyPair keyPair);
|
||||
|
||||
ListResponse<BaseRolePermissionResponse> createKeypairPermissionsResponse(List<ApiKeyPairPermission> permissions);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public class CreatePhysicalNetworkCmd extends BaseAsyncCreateCmd {
|
|||
@Parameter(name = ApiConstants.ISOLATION_METHODS,
|
||||
type = CommandType.LIST,
|
||||
collectionType = CommandType.STRING,
|
||||
description = "The isolation method for the physical Network[VLAN/L3/GRE]")
|
||||
description = "The isolation method for the physical Network[VLAN/VXLAN/GRE/STT/BCF_SEGMENT/SSP/ODL/L3VPN/VCS/NSX/NETRIS]")
|
||||
private List<String> isolationMethods;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "The name of the physical Network")
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import static org.apache.cloudstack.api.ApiConstants.S3_END_POINT;
|
|||
import static org.apache.cloudstack.api.ApiConstants.S3_HTTPS_FLAG;
|
||||
import static org.apache.cloudstack.api.ApiConstants.S3_MAX_ERROR_RETRY;
|
||||
import static org.apache.cloudstack.api.ApiConstants.S3_SIGNER;
|
||||
import static org.apache.cloudstack.api.ApiConstants.S3_SECRET_KEY;
|
||||
import static org.apache.cloudstack.api.ApiConstants.SECRET_KEY;
|
||||
import static org.apache.cloudstack.api.ApiConstants.S3_SOCKET_TIMEOUT;
|
||||
import static org.apache.cloudstack.api.ApiConstants.S3_USE_TCP_KEEPALIVE;
|
||||
import static org.apache.cloudstack.api.BaseCmd.CommandType.BOOLEAN;
|
||||
|
|
@ -64,7 +64,7 @@ public final class AddImageStoreS3CMD extends BaseCmd implements ClientOptions {
|
|||
@Parameter(name = S3_ACCESS_KEY, type = STRING, required = true, description = "S3 access key")
|
||||
private String accessKey;
|
||||
|
||||
@Parameter(name = S3_SECRET_KEY, type = STRING, required = true, description = "S3 secret key")
|
||||
@Parameter(name = SECRET_KEY, type = STRING, required = true, description = "S3 secret key")
|
||||
private String secretKey;
|
||||
|
||||
@Parameter(name = S3_END_POINT, type = STRING, required = true, description = "S3 endpoint")
|
||||
|
|
@ -101,7 +101,7 @@ public final class AddImageStoreS3CMD extends BaseCmd implements ClientOptions {
|
|||
Map<String, String> dm = new HashMap();
|
||||
|
||||
dm.put(ApiConstants.S3_ACCESS_KEY, getAccessKey());
|
||||
dm.put(ApiConstants.S3_SECRET_KEY, getSecretKey());
|
||||
dm.put(ApiConstants.SECRET_KEY, getSecretKey());
|
||||
dm.put(ApiConstants.S3_END_POINT, getEndPoint());
|
||||
dm.put(ApiConstants.S3_BUCKET_NAME, getBucketName());
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
// 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.apache.cloudstack.api.command.admin.user;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
|
||||
@APICommand(name = "deleteUserKeys", description = "Deletes a keypair from a user", responseObject = SuccessResponse.class,
|
||||
since = "4.23.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class DeleteUserKeysCmd extends BaseAsyncCmd {
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID, entityType = ApiKeyPairResponse.class, required = true, description = "ID of the keypair to be deleted.")
|
||||
private Long id;
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.User;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
ApiKeyPair keyPair = apiKeyPairService.findById(id);
|
||||
if (keyPair != null) {
|
||||
return keyPair.getAccountId();
|
||||
}
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
_accountService.deleteApiKey(this);
|
||||
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
this.setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_DELETE_SECRET_API_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
ApiKeyPair keyPair = apiKeyPairService.findById(id);
|
||||
return String.format("Deleting API key pair with ID [%s]%s",
|
||||
keyPair == null ? id : keyPair.getUuid(),
|
||||
keyPair == null ? "." : String.format(" and name [%s].", keyPair.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSyncObjId() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -32,33 +32,33 @@ import org.apache.cloudstack.api.response.UserResponse;
|
|||
import java.util.Map;
|
||||
|
||||
@APICommand(name = "getUserKeys",
|
||||
description = "This command allows the user to query the seceret and API keys for the account",
|
||||
responseObject = RegisterUserKeyResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = true,
|
||||
authorized = {RoleType.User, RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin},
|
||||
since = "4.10.0")
|
||||
|
||||
public class GetUserKeysCmd extends BaseCmd{
|
||||
|
||||
@Parameter(name= ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user whose keys are required")
|
||||
description = "Queries the last registered secret and API keys of a user.",
|
||||
responseObject = RegisterUserKeyResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = true,
|
||||
authorized = {RoleType.User, RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin},
|
||||
since = "4.10.0")
|
||||
public class GetUserKeysCmd extends BaseCmd {
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user whose keys are required")
|
||||
private Long id;
|
||||
|
||||
|
||||
public Long getID(){
|
||||
public Long getId() {
|
||||
return id;
|
||||
}public long getEntityOwnerId(){
|
||||
User user = _entityMgr.findById(User.class, getID());
|
||||
if(user != null){
|
||||
}
|
||||
|
||||
public long getEntityOwnerId() {
|
||||
User user = _entityMgr.findById(User.class, getId());
|
||||
if (user != null) {
|
||||
return user.getAccountId();
|
||||
}
|
||||
else return Account.ACCOUNT_ID_SYSTEM;
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
public void execute(){
|
||||
|
||||
public void execute() {
|
||||
Pair<Boolean, Map<String, String>> keys = _accountService.getKeys(this);
|
||||
|
||||
RegisterUserKeyResponse response = new RegisterUserKeyResponse();
|
||||
if(keys != null){
|
||||
if (keys != null){
|
||||
response.setApiKeyAccess(keys.first());
|
||||
response.setApiKey(keys.second().get("apikey"));
|
||||
response.setSecretKey(keys.second().get("secretkey"));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// 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.apache.cloudstack.api.command.admin.user;
|
||||
|
||||
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.BaseRolePermissionResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = "listUserKeyRules",
|
||||
description = "Lists the rules defined for a API key pair.",
|
||||
responseObject = BaseRolePermissionResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
since = "4.23.0")
|
||||
public class ListUserKeyRulesCmd extends BaseCmd {
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID, entityType = ApiKeyPairResponse.class, description = "ID of the key pair.", required = true)
|
||||
private Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getEntityOwnerId() {
|
||||
ApiKeyPair keyPair = apiKeyPairService.findById(getId());
|
||||
if (keyPair != null) {
|
||||
return keyPair.getAccountId();
|
||||
}
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException {
|
||||
List<ApiKeyPairPermission> permissions = _accountService.listKeyRules(this);
|
||||
ListResponse<BaseRolePermissionResponse> response = _responseGenerator.createKeypairPermissionsResponse(permissions);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// 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.apache.cloudstack.api.command.admin.user;
|
||||
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.UserAccount;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
|
||||
@APICommand(name = "listUserKeys",
|
||||
description = "Lists the API key pairs (API and secret keys) of a user.",
|
||||
responseObject = ApiKeyPairResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = true,
|
||||
authorized = {RoleType.User, RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin},
|
||||
since = "4.23.0")
|
||||
public class ListUserKeysCmd extends BaseListCmd {
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "ID of the user that owns the keys.")
|
||||
private Long userId;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID, entityType = ApiKeyPairResponse.class, description = "ID of the key pair.")
|
||||
private Long keyPairId;
|
||||
|
||||
@Parameter(name = ApiConstants.API_KEY_FILTER, type = CommandType.STRING, description = "API key of the key pair.")
|
||||
private String apiKeyFilter;
|
||||
|
||||
@Parameter(name = ApiConstants.SHOW_PERMISSIONS, type = CommandType.BOOLEAN, description = "Whether API Key rules should be returned. Defaults to false.")
|
||||
private boolean showPermissions = false;
|
||||
|
||||
@Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin},
|
||||
description = "Lists all API key pairs of users that are accessible by the calling account. Only available for administrators. Defaults to false.")
|
||||
private boolean listAll = false;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Long getKeyId() {
|
||||
return keyPairId;
|
||||
}
|
||||
|
||||
public String getApiKeyFilter() {
|
||||
return apiKeyFilter;
|
||||
}
|
||||
|
||||
public Boolean getShowPermissions() {
|
||||
return showPermissions;
|
||||
}
|
||||
|
||||
public boolean getListAll() {
|
||||
return listAll;
|
||||
}
|
||||
|
||||
public long getEntityOwnerId() {
|
||||
if (getKeyId() != null) {
|
||||
ApiKeyPair keypair = apiKeyPairService.findById(getKeyId());
|
||||
if (keypair != null) {
|
||||
return keypair.getAccountId();
|
||||
}
|
||||
} else if (getUserId() != null) {
|
||||
UserAccount userAccount = _accountService.getUserAccountById(getUserId());
|
||||
if (userAccount != null) {
|
||||
return userAccount.getAccountId();
|
||||
}
|
||||
}
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
ListResponse<ApiKeyPairResponse> finalResponse = _accountService.listKeys(this);
|
||||
finalResponse.setObjectName("userkeys");
|
||||
finalResponse.setResponseName(getCommandName());
|
||||
setResponseObject(finalResponse);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
// 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.apache.cloudstack.api.command.admin.user;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.RegisterUserKeyResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
|
||||
@APICommand(name = "registerUserKeys",
|
||||
responseObject = RegisterUserKeyResponse.class,
|
||||
description = "This command allows a user to register for the developer API, returning a secret key and an API key. This request is made through the integration API port, so it is a privileged command and must be made on behalf of a user. It is up to the implementer just how the username and password are entered, and then how that translates to an integration API request. Both secret key and API key should be returned to the user",
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
|
||||
public class RegisterUserKeyCmd extends BaseCmd {
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "User id")
|
||||
private Long id;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
User user = _entityMgr.findById(User.class, getId());
|
||||
if (user != null) {
|
||||
return user.getAccountId();
|
||||
}
|
||||
|
||||
return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.User;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
String[] keys = _accountService.createApiKeyAndSecretKey(this);
|
||||
RegisterUserKeyResponse response = new RegisterUserKeyResponse();
|
||||
if (keys != null) {
|
||||
response.setApiKey(keys[0]);
|
||||
response.setSecretKey(keys[1]);
|
||||
}
|
||||
response.setObjectName("userkeys");
|
||||
response.setResponseName(getCommandName());
|
||||
this.setResponseObject(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
// 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.apache.cloudstack.api.command.admin.user;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import org.apache.cloudstack.acl.Rule;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@APICommand(name = "registerUserKeys",
|
||||
responseObject = ApiKeyPairResponse.class,
|
||||
description = "Registers an API key pair (API and secret keys) for a user.",
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
|
||||
public class RegisterUserKeysCmd extends BaseAsyncCmd {
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserResponse.class, required = true, description = "ID of the user.")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "API key pair name.")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "API key pair description.")
|
||||
private String description;
|
||||
|
||||
@Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Start date of the API key pair. " +
|
||||
ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS)
|
||||
private Date startDate;
|
||||
|
||||
@Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "Expiration date of the API key pair. " +
|
||||
ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS)
|
||||
private Date endDate;
|
||||
|
||||
@Parameter(name = ApiConstants.RULES, type = CommandType.MAP, description = "The rules of the API key pair. If no rules are informed, " +
|
||||
"defaults to allowing all account permissions. Otherwise, only the explicitly informed permissions for the key pair will be " +
|
||||
"considered. Lower indexed rules take precedence over higher. Thus, in the following example: " +
|
||||
"\"rules[0].rule=deleteUserKeys rules[0].permission=deny rules[1].rule=*UserKey* rules[1].permission=allow\", all rules matching " +
|
||||
"the expression \"*UserKeys*\" will be allowed, except for \"deleteUserKeys\".")
|
||||
private Map rules;
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.id = userId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public void setRules(Map rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public List<Map<String, Object>> getRules() {
|
||||
List<Map<String, Object>> rulesDetails = new ArrayList<>();
|
||||
|
||||
if (rules == null) {
|
||||
return rulesDetails;
|
||||
}
|
||||
|
||||
for (Object ruleObject : rules.values()) {
|
||||
HashMap<String, String> detail = (HashMap<String, String>) ruleObject;
|
||||
Map<String, Object> ruleDetails = new HashMap<>();
|
||||
|
||||
String rule = detail.get(ApiConstants.RULE);
|
||||
if (StringUtils.isEmpty(rule)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Empty rule has been provided in the [rules] parameter.");
|
||||
}
|
||||
|
||||
String permission = detail.get(ApiConstants.PERMISSION);
|
||||
if (StringUtils.isEmpty(permission)) {
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Rule [%s] has no permission associated with it," +
|
||||
" please specify if it is either [allow] or [deny].", rule));
|
||||
}
|
||||
ruleDetails.put(ApiConstants.RULE, new Rule(rule));
|
||||
ruleDetails.put(ApiConstants.PERMISSION, roleService.getRolePermission(permission));
|
||||
|
||||
String description = detail.get(ApiConstants.DESCRIPTION);
|
||||
if (StringUtils.isNotEmpty(description)) {
|
||||
ruleDetails.put(ApiConstants.DESCRIPTION, description);
|
||||
}
|
||||
|
||||
rulesDetails.add(ruleDetails);
|
||||
}
|
||||
return rulesDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
User user = _entityMgr.findById(User.class, getUserId());
|
||||
List<Long> accessibleUsers = _queryService.searchForAccessibleUsers();
|
||||
if (user != null && accessibleUsers.stream().anyMatch(u -> u == user.getId())) {
|
||||
return user.getAccountId();
|
||||
}
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
User user = _entityMgr.findById(User.class, getUserId());
|
||||
if (user != null) {
|
||||
return user.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.User;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ApiKeyPair apiKeyPair = _accountService.createApiKeyAndSecretKey(this);
|
||||
ApiKeyPairResponse response = new ApiKeyPairResponse();
|
||||
if (apiKeyPair != null) {
|
||||
response = _responseGenerator.createKeyPairResponse(apiKeyPair);
|
||||
}
|
||||
response.setObjectName("userkeys");
|
||||
response.setResponseName(getCommandName());
|
||||
this.setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_REGISTER_FOR_SECRET_API_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
String userUuid = getResourceUuid(ApiConstants.ID);
|
||||
return String.format("Registering API keypair for user [%s].", userUuid == null ? id : userUuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyncObjType() {
|
||||
return BaseAsyncCmd.user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSyncObjId() {
|
||||
return getUserId();
|
||||
}
|
||||
}
|
||||
|
|
@ -46,8 +46,8 @@ public class UpdateUserCmd extends BaseCmd {
|
|||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, description = "The API key for the user. Must be specified with userSecretKey")
|
||||
private String apiKey;
|
||||
@Parameter(name = ApiConstants.USER_API_KEY, type = CommandType.STRING, description = "Updates the latest API key of the user. Must be specified with usersecretkey")
|
||||
private String userApiKey;
|
||||
|
||||
@Parameter(name = ApiConstants.EMAIL, type = CommandType.STRING, description = "Email")
|
||||
private String email;
|
||||
|
|
@ -70,8 +70,8 @@ public class UpdateUserCmd extends BaseCmd {
|
|||
@Parameter(name = ApiConstants.CURRENT_PASSWORD, type = CommandType.STRING, description = "Current password that was being used by the user. You must inform the current password when updating the password.", acceptedOnAdminPort = false)
|
||||
private String currentPassword;
|
||||
|
||||
@Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey")
|
||||
private String secretKey;
|
||||
@Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "Updates the latest secret key of the user. Must be specified with userapikey.")
|
||||
private String userSecretKey;
|
||||
|
||||
@Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "Determines if Api key access for this user is enabled, disabled or inherits the value from its parent, the owning account", since = "4.20.1.0", authorized = {RoleType.Admin})
|
||||
private String apiKeyAccess;
|
||||
|
|
@ -99,7 +99,7 @@ public class UpdateUserCmd extends BaseCmd {
|
|||
/////////////////////////////////////////////////////
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
return userApiKey;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
|
|
@ -127,7 +127,7 @@ public class UpdateUserCmd extends BaseCmd {
|
|||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
return userSecretKey;
|
||||
}
|
||||
|
||||
public String getApiKeyAccess() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,285 @@
|
|||
// 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.apache.cloudstack.api.response;
|
||||
|
||||
import com.cloud.user.ApiKeyPairState;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import org.apache.cloudstack.api.BaseResponseWithAnnotations;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
|
||||
@EntityReference(value = ApiKeyPair.class)
|
||||
public class ApiKeyPairResponse extends BaseResponseWithAnnotations {
|
||||
@SerializedName(ApiConstants.NAME)
|
||||
@Param(description = "Name of the API key pair")
|
||||
private String name;
|
||||
|
||||
@SerializedName(ApiConstants.API_KEY)
|
||||
@Param(description = "The API key of the registered user.", isSensitive = true)
|
||||
private String userApiKey;
|
||||
|
||||
@SerializedName(ApiConstants.SECRET_KEY)
|
||||
@Param(description = "The secret key of the registered user.", isSensitive = true)
|
||||
private String userSecretKey;
|
||||
|
||||
@SerializedName(ApiConstants.USER_ID)
|
||||
@Param(description = "ID of the user that owns the keypair.")
|
||||
private String userId;
|
||||
|
||||
@SerializedName(ApiConstants.USERNAME)
|
||||
@Param(description = "Username of the keypair's owner.")
|
||||
private String username;
|
||||
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "ID of the API key pair.", isSensitive = true)
|
||||
private String id;
|
||||
|
||||
@SerializedName(ApiConstants.DESCRIPTION)
|
||||
@Param(description = "API key pair description.")
|
||||
private String description;
|
||||
|
||||
@SerializedName(ApiConstants.START_DATE)
|
||||
@Param(description = "API key pair start date.")
|
||||
private Date startDate;
|
||||
|
||||
@SerializedName(ApiConstants.END_DATE)
|
||||
@Param(description = "API key pair expiration date.")
|
||||
private Date endDate;
|
||||
|
||||
@SerializedName(ApiConstants.CREATED)
|
||||
@Param(description = "API key pair creation timestamp.")
|
||||
private Date created;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_TYPE)
|
||||
@Param(description = "Account type.")
|
||||
private String accountType;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_ID)
|
||||
@Param(description = "Account ID.")
|
||||
private String accountId;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_NAME)
|
||||
@Param(description = "Account name.")
|
||||
private String accountName;
|
||||
|
||||
@SerializedName(ApiConstants.ROLE_ID)
|
||||
@Param(description = "ID of the role.")
|
||||
private String roleId;
|
||||
|
||||
@SerializedName(ApiConstants.ROLE_TYPE)
|
||||
@Param(description = "Type of the role (Admin, ResourceAdmin, DomainAdmin, User).")
|
||||
private String roleType;
|
||||
|
||||
@SerializedName(ApiConstants.ROLE_NAME)
|
||||
@Param(description = "Name of the role.")
|
||||
private String roleName;
|
||||
|
||||
@SerializedName(ApiConstants.PERMISSIONS)
|
||||
@Param(description = "Permissions of the API key pair.")
|
||||
private List<BaseRolePermissionResponse> permissions;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||
@Param(description = "ID of the domain which the account belongs to.")
|
||||
private String domainId;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN)
|
||||
@Param(description = "Name of the domain which the account belongs to.")
|
||||
private String domainName;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_PATH)
|
||||
@Param(description = "Path of the domain which the account belongs to.")
|
||||
private String domainPath;
|
||||
|
||||
@SerializedName(ApiConstants.STATE)
|
||||
@Param(description = "State of the API key pair.")
|
||||
private ApiKeyPairState state;
|
||||
|
||||
public String getApiKey() {
|
||||
return userApiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.userApiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return userSecretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.userSecretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getAccountType() {
|
||||
return accountType;
|
||||
}
|
||||
|
||||
public void setAccountType(String accountType) {
|
||||
this.accountType = accountType;
|
||||
}
|
||||
|
||||
public String getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(String roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getRoleType() {
|
||||
return roleType;
|
||||
}
|
||||
|
||||
public void setRoleType(String roleType) {
|
||||
this.roleType = roleType;
|
||||
}
|
||||
|
||||
public String getRoleName() {
|
||||
return roleName;
|
||||
}
|
||||
|
||||
public void setRoleName(String roleName) {
|
||||
this.roleName = roleName;
|
||||
}
|
||||
|
||||
public String getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public void setDomainId(String domainId) {
|
||||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public void setDomainName(String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public String getDomainPath() {
|
||||
return domainPath;
|
||||
}
|
||||
|
||||
public void setDomainPath(String domainPath) {
|
||||
this.domainPath = domainPath;
|
||||
}
|
||||
|
||||
public ApiKeyPairState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(ApiKeyPairState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public void setAccountName(String accountName) {
|
||||
this.accountName = accountName;
|
||||
}
|
||||
|
||||
public List<BaseRolePermissionResponse> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public void setPermissions(List<BaseRolePermissionResponse> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
}
|
||||
|
|
@ -95,12 +95,12 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons
|
|||
@Param(description = "The timezone user was created in")
|
||||
private String timezone;
|
||||
|
||||
@SerializedName("apikey")
|
||||
@SerializedName(ApiConstants.API_KEY)
|
||||
@Param(description = "The API key of the user", isSensitive = true)
|
||||
private String apiKey;
|
||||
|
||||
@Deprecated
|
||||
@SerializedName("secretkey")
|
||||
@SerializedName(ApiConstants.SECRET_KEY)
|
||||
@Param(description = "The secret key of the user", isSensitive = true)
|
||||
private String secretKey;
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ public interface QueryService {
|
|||
|
||||
ListResponse<UserResponse> searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException;
|
||||
|
||||
List<Long> searchForAccessibleUsers();
|
||||
|
||||
ListResponse<EventResponse> searchForEvents(ListEventsCmd cmd);
|
||||
|
||||
ListResponse<ResourceTagResponse> listTags(ListTagsCmd cmd);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,46 @@
|
|||
package org.apache.cloudstack.acl;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
|
||||
public class RuleTest {
|
||||
|
||||
private static List<String> apiNames;
|
||||
private static List<Rule> apiRules;
|
||||
private static ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
provider.addIncludeFilter(new AnnotationTypeFilter(APICommand.class));
|
||||
Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents("org.apache.cloudstack.api");
|
||||
|
||||
apiNames = new ArrayList<>();
|
||||
apiRules = new ArrayList<>();
|
||||
for(BeanDefinition bd : beanDefinitions) {
|
||||
if (bd instanceof AnnotatedBeanDefinition) {
|
||||
Map<String, Object> annotationAttributeMap = ((AnnotatedBeanDefinition) bd).getMetadata()
|
||||
.getAnnotationAttributes(APICommand.class.getName());
|
||||
String apiName = annotationAttributeMap.get("name").toString();
|
||||
apiNames.add(apiName);
|
||||
apiRules.add(new Rule(apiName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() throws Exception {
|
||||
Rule rule = new Rule("someString");
|
||||
|
|
@ -31,21 +64,89 @@ public class RuleTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesEmpty() throws Exception {
|
||||
Rule rule = new Rule("someString");
|
||||
Assert.assertFalse(rule.matches(""));
|
||||
public void ruleMatchesTestNoMatchesOnEmptyString() throws Exception {
|
||||
String testCmd = "";
|
||||
List<String> matches = new ArrayList<>();
|
||||
for (Rule rule : apiRules) {
|
||||
if (rule.matches(testCmd)) {
|
||||
matches.add(rule.getRuleString());
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(matches.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesNull() throws Exception {
|
||||
Rule rule = new Rule("someString");
|
||||
Assert.assertFalse(rule.matches(null));
|
||||
public void ruleMatchesTestNoMatchesOnNull() throws Exception {
|
||||
List<String> matches = new ArrayList<>();
|
||||
for (Rule rule : apiRules) {
|
||||
if (rule.matches(null)) {
|
||||
matches.add(rule.getRuleString());
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(matches.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesSpace() throws Exception {
|
||||
Rule rule = new Rule("someString");
|
||||
Assert.assertFalse(rule.matches(" "));
|
||||
public void ruleMatchesTestNoMatchesOnSpaceCharacter() throws Exception {
|
||||
String testCmd = " ";
|
||||
List<String> matches = new ArrayList<>();
|
||||
for (Rule rule : apiRules) {
|
||||
if (rule.matches(testCmd)) {
|
||||
matches.add(rule.getRuleString());
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(matches.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleMatchesTestWildCardOnEndWorksAsNormalRegex() {
|
||||
setup();
|
||||
Pattern regexPattern = Pattern.compile("list.*");
|
||||
Rule acsRegexRule = new Rule("list*");
|
||||
|
||||
List<String> nonMatches = new ArrayList<>();
|
||||
for (String apiName : apiNames) {
|
||||
if (acsRegexRule.matches(apiName) != regexPattern.matcher(apiName).matches()) {
|
||||
nonMatches.add(apiName);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(nonMatches.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleMatchesTestWildCardOnMiddleWorksAsNormalRegex() {
|
||||
setup();
|
||||
Pattern regexPattern = Pattern.compile("list.*s");
|
||||
Rule acsRegexRule = new Rule("list*s");
|
||||
|
||||
List<String> nonMatches = new ArrayList<>();
|
||||
for (String apiName : apiNames) {
|
||||
if (acsRegexRule.matches(apiName) != regexPattern.matcher(apiName).matches()) {
|
||||
nonMatches.add(apiName);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(nonMatches.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleMatchesTestWildCardOnStartWorksAsNormalRegex() {
|
||||
setup();
|
||||
Pattern regexPattern = Pattern.compile(".*User");
|
||||
Rule acsRegexRule = new Rule("*User");
|
||||
|
||||
List<String> nonMatches = new ArrayList<>();
|
||||
for (String apiName : apiNames) {
|
||||
if (acsRegexRule.matches(apiName) != regexPattern.matcher(apiName).matches()) {
|
||||
nonMatches.add(apiName);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(nonMatches.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -73,7 +174,25 @@ public class RuleTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRuleWithValidData() throws Exception {
|
||||
public void ruleMatchesTestWildcardOnRuleAndCommand() throws Exception {
|
||||
Rule rule = new Rule("*");
|
||||
Assert.assertTrue(rule.matches("list*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleMatchesTestWildcardOnRuleAndCommandNotAllowed() throws Exception {
|
||||
Rule rule = new Rule("list*");
|
||||
Assert.assertFalse(rule.matches("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ruleMatchesTestWithMultipleStars() throws Exception {
|
||||
Rule rule = new Rule("list***");
|
||||
Assert.assertFalse(rule.matches("api"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleToStringWithValidStrings() throws Exception {
|
||||
for (String rule : Arrays.asList("a", "1", "someApi", "someApi321", "123SomeApi",
|
||||
"prefix*", "*middle*", "*Suffix",
|
||||
"*", "**", "f***", "m0nk3yMa**g1c*")) {
|
||||
|
|
@ -82,7 +201,7 @@ public class RuleTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRuleWithInvalidData() throws Exception {
|
||||
public void testRuleToStringWithInvalidStrings() throws Exception {
|
||||
for (String rule : Arrays.asList(null, "", " ", " ", "\n", "\t", "\r", "\"", "\'",
|
||||
"^someApi$", "^someApi", "some$", "some-Api;", "some,Api",
|
||||
"^", "$", "^$", ".*", "\\w+", "r**l3rd0@Kr3", "j@s1n|+|0ȷ",
|
||||
|
|
|
|||
|
|
@ -1947,7 +1947,7 @@ public class Upgrade410to420 extends DbUpgradeAbstractImpl {
|
|||
|
||||
Map<String, String> detailMap = new HashMap<String, String>();
|
||||
detailMap.put(ApiConstants.S3_ACCESS_KEY, s3_accesskey);
|
||||
detailMap.put(ApiConstants.S3_SECRET_KEY, s3_secretkey);
|
||||
detailMap.put(ApiConstants.SECRET_KEY, s3_secretkey);
|
||||
detailMap.put(ApiConstants.S3_BUCKET_NAME, s3_bucket);
|
||||
detailMap.put(ApiConstants.S3_END_POINT, s3_endpoint);
|
||||
detailMap.put(ApiConstants.S3_HTTPS_FLAG, String.valueOf(s3_https));
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.utils.db.Encrypt;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
@Entity
|
||||
|
|
@ -69,13 +68,6 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
|||
@Column(name = "state")
|
||||
private String state;
|
||||
|
||||
@Column(name = "api_key")
|
||||
private String apiKey = null;
|
||||
|
||||
@Encrypt
|
||||
@Column(name = "secret_key")
|
||||
private String secretKey = null;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN)
|
||||
private Date created;
|
||||
|
||||
|
|
@ -203,24 +195,6 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
|||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import com.cloud.user.Account.State;
|
||||
import com.cloud.utils.db.Encrypt;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
|
@ -71,13 +70,6 @@ public class UserVO implements User, Identity, InternalIdentity {
|
|||
@Enumerated(value = EnumType.STRING)
|
||||
private State state;
|
||||
|
||||
@Column(name = "api_key")
|
||||
private String apiKey = null;
|
||||
|
||||
@Encrypt
|
||||
@Column(name = "secret_key")
|
||||
private String secretKey = null;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN)
|
||||
private Date created;
|
||||
|
||||
|
|
@ -150,8 +142,6 @@ public class UserVO implements User, Identity, InternalIdentity {
|
|||
this.setTimezone(user.getTimezone());
|
||||
this.setUuid(user.getUuid());
|
||||
this.setSource(user.getSource());
|
||||
this.setApiKey(user.getApiKey());
|
||||
this.setSecretKey(user.getSecretKey());
|
||||
this.setExternalEntity(user.getExternalEntity());
|
||||
this.setRegistered(user.isRegistered());
|
||||
this.setRegistrationToken(user.getRegistrationToken());
|
||||
|
|
@ -243,26 +233,6 @@ public class UserVO implements User, Identity, InternalIdentity {
|
|||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTimezone() {
|
||||
if (StringUtils.isEmpty(timezone)) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,4 @@ public interface UserAccountDao extends GenericDao<UserAccountVO, Long> {
|
|||
List<UserAccountVO> getUserAccountByEmail(String email, Long domainId);
|
||||
|
||||
boolean validateUsernameInDomain(String username, Long domainId);
|
||||
|
||||
UserAccount getUserByApiKey(String apiKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package com.cloud.user.dao;
|
|||
import com.cloud.user.UserAccount;
|
||||
import com.cloud.user.UserAccountVO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -28,14 +27,6 @@ import java.util.List;
|
|||
@Component
|
||||
public class UserAccountDaoImpl extends GenericDaoBase<UserAccountVO, Long> implements UserAccountDao {
|
||||
|
||||
protected final SearchBuilder<UserAccountVO> userAccountSearch;
|
||||
|
||||
public UserAccountDaoImpl() {
|
||||
userAccountSearch = createSearchBuilder();
|
||||
userAccountSearch.and("apiKey", userAccountSearch.entity().getApiKey(), SearchCriteria.Op.EQ);
|
||||
userAccountSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserAccountVO> getAllUsersByNameAndEntity(String username, String entity) {
|
||||
if (username == null) {
|
||||
|
|
@ -79,12 +70,4 @@ public class UserAccountDaoImpl extends GenericDaoBase<UserAccountVO, Long> impl
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAccount getUserByApiKey(String apiKey) {
|
||||
SearchCriteria<UserAccountVO> sc = userAccountSearch.create();
|
||||
sc.setParameters("apiKey", apiKey);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,6 @@ public interface UserDao extends GenericDao<UserVO, Long> {
|
|||
|
||||
List<UserVO> listByAccount(long accountId);
|
||||
|
||||
/**
|
||||
* Finds a user based on the secret key provided.
|
||||
* @param secretKey
|
||||
* @return
|
||||
*/
|
||||
UserVO findUserBySecretKey(String secretKey);
|
||||
|
||||
/**
|
||||
* Finds a user based on the registration token provided.
|
||||
* @param registrationToken
|
||||
|
|
|
|||
|
|
@ -65,10 +65,6 @@ public class UserDaoImpl extends GenericDaoBase<UserVO, Long> implements UserDao
|
|||
UserIdSearch.and("id", UserIdSearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||
UserIdSearch.done();
|
||||
|
||||
SecretKeySearch = createSearchBuilder();
|
||||
SecretKeySearch.and("secretKey", SecretKeySearch.entity().getSecretKey(), SearchCriteria.Op.EQ);
|
||||
SecretKeySearch.done();
|
||||
|
||||
RegistrationTokenSearch = createSearchBuilder();
|
||||
RegistrationTokenSearch.and("registrationToken", RegistrationTokenSearch.entity().getRegistrationToken(), SearchCriteria.Op.EQ);
|
||||
RegistrationTokenSearch.done();
|
||||
|
|
@ -121,13 +117,6 @@ public class UserDaoImpl extends GenericDaoBase<UserVO, Long> implements UserDao
|
|||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO findUserBySecretKey(String secretKey) {
|
||||
SearchCriteria<UserVO> sc = SecretKeySearch.create();
|
||||
sc.setParameters("secretKey", secretKey);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserVO findUserByRegistrationToken(String registrationToken) {
|
||||
SearchCriteria<UserVO> sc = RegistrationTokenSearch.create();
|
||||
|
|
|
|||
|
|
@ -846,15 +846,18 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
|
|||
|
||||
try {
|
||||
pstmtLegacy = txn.prepareAutoCloseStatement(finalQueryLegacy.toString());
|
||||
pstmt = txn.prepareAutoCloseStatement(finalQuery.toString());
|
||||
for (int i = 0; i < resourceIdList.size(); i++) {
|
||||
pstmtLegacy.setLong(1 + i, resourceIdList.get(i));
|
||||
pstmt.setLong(1 + i, resourceIdList.get(i));
|
||||
}
|
||||
ResultSet rs = pstmtLegacy.executeQuery();
|
||||
while (rs.next()) {
|
||||
result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3));
|
||||
}
|
||||
|
||||
pstmt = txn.prepareAutoCloseStatement(finalQuery.toString());
|
||||
for (int i = 0; i < resourceIdList.size(); i++) {
|
||||
pstmt.setLong(1 + i, resourceIdList.get(i));
|
||||
}
|
||||
rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
// 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.apache.cloudstack.acl;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "api_keypair_permissions")
|
||||
public class ApiKeyPairPermissionVO extends RolePermissionBaseVO implements ApiKeyPairPermission {
|
||||
@Column(name = "api_keypair_id")
|
||||
private long apiKeyPairId;
|
||||
|
||||
@Column(name = "sort_order")
|
||||
private long sortOrder = 0;
|
||||
|
||||
public ApiKeyPairPermissionVO(long apiKeyPairId, String rule, Permission permission, String description) {
|
||||
super(rule, permission, description);
|
||||
this.apiKeyPairId = apiKeyPairId;
|
||||
}
|
||||
|
||||
public ApiKeyPairPermissionVO(String rule, Permission permission, String description) {
|
||||
super(rule, permission, description);
|
||||
}
|
||||
|
||||
public ApiKeyPairPermissionVO() {
|
||||
}
|
||||
|
||||
public long getApiKeyPairId() {
|
||||
return this.apiKeyPairId;
|
||||
}
|
||||
|
||||
public void setApiKeyPairId(long keyPairId) {
|
||||
this.apiKeyPairId = keyPairId;
|
||||
}
|
||||
|
||||
public void setSortOrder(long sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public long getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
// 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.apache.cloudstack.acl;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.db.Encrypt;
|
||||
import java.time.Instant;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "api_keypair")
|
||||
public class ApiKeyPairVO implements ApiKeyPair {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", nullable = false)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private String uuid = UUID.randomUUID().toString();
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "domain_id", nullable = false)
|
||||
private Long domainId;
|
||||
|
||||
@Column(name = "account_id", nullable = false)
|
||||
private Long accountId;
|
||||
|
||||
@Column(name = "start_date")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date startDate;
|
||||
|
||||
@Column(name = "end_date")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date endDate;
|
||||
|
||||
@Column(name = "created", nullable = false)
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date created = Date.from(Instant.now());
|
||||
|
||||
@Column(name = "description")
|
||||
private String description = "";
|
||||
|
||||
@Column(name = "api_key", nullable = false)
|
||||
private String apiKey;
|
||||
|
||||
@Encrypt
|
||||
@Column(name = "secret_key", nullable = false)
|
||||
private String secretKey;
|
||||
|
||||
@Column(name = "removed")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
public ApiKeyPairVO() {
|
||||
}
|
||||
|
||||
public ApiKeyPairVO(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ApiKeyPairVO(Long userId, String description, Date startDate, Date endDate,
|
||||
String apiKey, String secretKey) {
|
||||
this.userId = userId;
|
||||
this.description = description;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.apiKey = apiKey;
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public ApiKeyPairVO(String name, Long userId, String description, Date startDate, Date endDate, Account account) {
|
||||
this.name = Objects.requireNonNullElseGet(name, () -> userId + " - API key pair");
|
||||
this.userId = userId;
|
||||
this.description = description;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.domainId = account.getDomainId();
|
||||
this.accountId = account.getAccountId();
|
||||
}
|
||||
|
||||
public ApiKeyPairVO(Long id, Long userId) {
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public void validateDate() {
|
||||
Date now = DateTime.now().toDate();
|
||||
Date keypairStart = this.getStartDate();
|
||||
Date keypairExpiration = this.getEndDate();
|
||||
if (keypairStart != null && now.compareTo(keypairStart) <= 0) {
|
||||
throw new InvalidParameterValueException(String.format("API key pair is not valid yet, start date: %s", keypairStart));
|
||||
}
|
||||
if (keypairExpiration != null && now.compareTo(keypairExpiration) >= 0) {
|
||||
throw new InvalidParameterValueException(String.format("API key pair is expired, expiration date: %s", keypairExpiration));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasEndDatePassed() {
|
||||
Date now = DateTime.now().toDate();
|
||||
Date keypairExpiration = this.getEndDate();
|
||||
return keypairExpiration != null && now.compareTo(keypairExpiration) >= 0;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public Class<?> getEntityType() {
|
||||
return ApiKeyPair.class;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDomainId() {
|
||||
return this.domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAccountId() {
|
||||
return this.accountId;
|
||||
}
|
||||
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setDomainId(Long domainId) {
|
||||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
public void setAccountId(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,12 @@ public class RolePermissionBaseVO implements RolePermissionEntity {
|
|||
|
||||
public RolePermissionBaseVO() { this.uuid = UUID.randomUUID().toString(); }
|
||||
|
||||
public RolePermissionBaseVO(final String rule, final Permission permission) {
|
||||
this();
|
||||
this.rule = rule;
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public RolePermissionBaseVO(final String rule, final Permission permission, final String description) {
|
||||
this();
|
||||
this.rule = rule;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.apache.cloudstack.acl.dao;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import java.util.List;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd;
|
||||
|
||||
public interface ApiKeyPairDao extends GenericDao<ApiKeyPairVO, Long> {
|
||||
ApiKeyPairVO findBySecretKey(String secretKey);
|
||||
|
||||
ApiKeyPairVO findByApiKey(String apiKey);
|
||||
|
||||
ApiKeyPairVO findByUuid(String uuid);
|
||||
|
||||
Pair<List<ApiKeyPairVO>, Integer> listApiKeysByUserOrApiKeyId(Long userId, Long apiKeyId);
|
||||
|
||||
ApiKeyPairVO getLastApiKeyCreatedByUser(Long userId);
|
||||
|
||||
Pair<List<ApiKeyPairVO>, Integer> listByUserIdsPaginated(List<Long> userIds, ListUserKeysCmd cmd);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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.apache.cloudstack.acl.dao;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import java.util.List;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairVO;
|
||||
import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ApiKeyPairDaoImpl extends GenericDaoBase<ApiKeyPairVO, Long> implements ApiKeyPairDao {
|
||||
private static final String ID = "id";
|
||||
private static final String USER_ID = "userId";
|
||||
private static final String API_KEY = "apiKey";
|
||||
private static final String SECRET_KEY = "secretKey";
|
||||
|
||||
private final SearchBuilder<ApiKeyPairVO> keyPairSearch;
|
||||
|
||||
ApiKeyPairDaoImpl() {
|
||||
super();
|
||||
|
||||
keyPairSearch = createSearchBuilder();
|
||||
keyPairSearch.and(API_KEY, keyPairSearch.entity().getApiKey(), SearchCriteria.Op.EQ);
|
||||
keyPairSearch.and(SECRET_KEY, keyPairSearch.entity().getSecretKey(), SearchCriteria.Op.EQ);
|
||||
keyPairSearch.and(ID, keyPairSearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||
keyPairSearch.and(USER_ID, keyPairSearch.entity().getUserId(), SearchCriteria.Op.IN);
|
||||
keyPairSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiKeyPairVO findByApiKey(String apiKey) {
|
||||
SearchCriteria<ApiKeyPairVO> sc = keyPairSearch.create();
|
||||
sc.setParameters(API_KEY, apiKey);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
public ApiKeyPairVO findBySecretKey(String secretKey) {
|
||||
SearchCriteria<ApiKeyPairVO> sc = keyPairSearch.create();
|
||||
sc.setParameters(SECRET_KEY, secretKey);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
public Pair<List<ApiKeyPairVO>, Integer> listApiKeysByUserOrApiKeyId(Long userId, Long apiKeyId) {
|
||||
SearchCriteria<ApiKeyPairVO> sc = keyPairSearch.create();
|
||||
sc.setParametersIfNotNull(USER_ID, userId);
|
||||
sc.setParametersIfNotNull(ID, apiKeyId);
|
||||
final Filter searchFilter = new Filter(100);
|
||||
return searchAndCount(sc, searchFilter);
|
||||
}
|
||||
|
||||
public ApiKeyPairVO getLastApiKeyCreatedByUser(Long userId) {
|
||||
final SearchCriteria<ApiKeyPairVO> sc = keyPairSearch.create();
|
||||
sc.setParametersIfNotNull(USER_ID, userId);
|
||||
final Filter searchBySorted = new Filter(ApiKeyPairVO.class, ID, false, null, null);
|
||||
return findOneBy(sc, searchBySorted);
|
||||
}
|
||||
|
||||
public Pair<List<ApiKeyPairVO>, Integer> listByUserIdsPaginated(List<Long> userIds, ListUserKeysCmd cmd) {
|
||||
Long pageSizeVal = cmd.getPageSizeVal();
|
||||
Long startIndex = cmd.getStartIndex();
|
||||
Filter searchFilter = new Filter(ApiKeyPairVO.class, ID, true, startIndex, pageSizeVal);
|
||||
|
||||
final SearchCriteria<ApiKeyPairVO> sc = keyPairSearch.create();
|
||||
sc.setParameters(USER_ID, (Object[]) userIds.toArray(new Long[0]));
|
||||
|
||||
Pair<List<ApiKeyPairVO>, Integer> apiKeyPairVOList = searchAndCount(sc, searchFilter);
|
||||
if (CollectionUtils.isEmpty(apiKeyPairVOList.first())) {
|
||||
return new Pair<>(List.of(), 0);
|
||||
}
|
||||
return apiKeyPairVOList;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.apache.cloudstack.acl.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairPermissionVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ApiKeyPairPermissionsDao extends GenericDao<ApiKeyPairPermissionVO, Long> {
|
||||
List<ApiKeyPairPermissionVO> findAllByApiKeyPairId(Long apiKeyPairId);
|
||||
|
||||
List<ApiKeyPairPermissionVO> findAllByKeyPairIdSorted(Long apiKeyPairId);
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 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.apache.cloudstack.acl.dao;
|
||||
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairPermissionVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class ApiKeyPairPermissionsDaoImpl extends GenericDaoBase<ApiKeyPairPermissionVO, Long> implements ApiKeyPairPermissionsDao {
|
||||
private static final String API_KEY_PAIR_ID = "apiKeyPairId";
|
||||
private static final String SORT_ORDER = "sortOrder";
|
||||
|
||||
private final SearchBuilder<ApiKeyPairPermissionVO> permissionByApiKeyPairIdSearch;
|
||||
|
||||
public ApiKeyPairPermissionsDaoImpl() {
|
||||
super();
|
||||
|
||||
permissionByApiKeyPairIdSearch = createSearchBuilder();
|
||||
permissionByApiKeyPairIdSearch.and(API_KEY_PAIR_ID, permissionByApiKeyPairIdSearch.entity().getApiKeyPairId(), SearchCriteria.Op.EQ);
|
||||
permissionByApiKeyPairIdSearch.done();
|
||||
}
|
||||
|
||||
public List<ApiKeyPairPermissionVO> findAllByApiKeyPairId(Long apiKeyPairId) {
|
||||
SearchCriteria<ApiKeyPairPermissionVO> sc = permissionByApiKeyPairIdSearch.create();
|
||||
sc.setParameters(API_KEY_PAIR_ID, String.valueOf(apiKeyPairId));
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiKeyPairPermissionVO persist(final ApiKeyPairPermissionVO item) {
|
||||
item.setSortOrder(0);
|
||||
final List<ApiKeyPairPermissionVO> permissionsList = findAllByKeyPairIdSorted(item.getApiKeyPairId());
|
||||
if (!CollectionUtils.isEmpty(permissionsList)) {
|
||||
ApiKeyPairPermissionVO lastPermission = permissionsList.get(permissionsList.size() - 1);
|
||||
item.setSortOrder(lastPermission.getSortOrder() + 1);
|
||||
}
|
||||
return super.persist(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApiKeyPairPermissionVO> findAllByKeyPairIdSorted(Long apiKeyPairId) {
|
||||
final SearchCriteria<ApiKeyPairPermissionVO> sc = permissionByApiKeyPairIdSearch.create();
|
||||
sc.setParameters(API_KEY_PAIR_ID, apiKeyPairId);
|
||||
final Filter searchBySorted = new Filter(ApiKeyPairPermissionVO.class, SORT_ORDER, true, null, null);
|
||||
final List<ApiKeyPairPermissionVO> apiKeyPairPermissionList = listBy(sc, searchBySorted);
|
||||
return Objects.requireNonNullElse(apiKeyPairPermissionList, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase<ImageStoreD
|
|||
for (ImageStoreDetailVO detail : details) {
|
||||
String name = detail.getName();
|
||||
String value = detail.getValue();
|
||||
if (name.equals(ApiConstants.KEY) || name.equals(ApiConstants.S3_SECRET_KEY)) {
|
||||
if (name.equals(ApiConstants.KEY) || name.equals(ApiConstants.SECRET_KEY)) {
|
||||
value = DBEncryptionUtil.decrypt(value);
|
||||
}
|
||||
detailsMap.put(name, value);
|
||||
|
|
|
|||
|
|
@ -310,6 +310,8 @@
|
|||
<bean id="gpuDeviceDaoImpl" class="com.cloud.gpu.dao.GpuDeviceDaoImpl" />
|
||||
<bean id="vgpuProfileDaoImpl" class="com.cloud.gpu.dao.VgpuProfileDaoImpl" />
|
||||
<bean id="importVMTaskDaoImpl" class="com.cloud.vm.dao.ImportVMTaskDaoImpl" />
|
||||
<bean id="apiKeyPairDaoImpl" class="org.apache.cloudstack.acl.dao.ApiKeyPairDaoImpl" />
|
||||
<bean id="apiKeyPairPermissionsDaoImpl" class="org.apache.cloudstack.acl.dao.ApiKeyPairPermissionsDaoImpl" />
|
||||
<bean id="dnsServerDao" class="org.apache.cloudstack.dns.dao.DnsServerDaoImpl" />
|
||||
<bean id="dnsZoneDao" class="org.apache.cloudstack.dns.dao.DnsZoneDaoImpl" />
|
||||
<bean id="dnsZoneNetworkMapDao" class="org.apache.cloudstack.dns.dao.DnsZoneNetworkMapDaoImpl" />
|
||||
|
|
|
|||
|
|
@ -34,6 +34,19 @@ CREATE TABLE `cloud`.`backup_offering_details` (
|
|||
UPDATE `cloud`.`configuration` SET value='random' WHERE name IN ('vm.allocation.algorithm', 'volume.allocation.algorithm') AND value='userconcentratedpod_random';
|
||||
UPDATE `cloud`.`configuration` SET value='firstfit' WHERE name IN ('vm.allocation.algorithm', 'volume.allocation.algorithm') AND value='userconcentratedpod_firstfit';
|
||||
|
||||
-- Create kubernetes_cluster_affinity_group_map table for CKS per-node-type affinity groups
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kubernetes_cluster_affinity_group_map` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`cluster_id` bigint unsigned NOT NULL COMMENT 'kubernetes cluster id',
|
||||
`node_type` varchar(32) NOT NULL COMMENT 'CONTROL, WORKER, or ETCD',
|
||||
`affinity_group_id` bigint unsigned NOT NULL COMMENT 'affinity group id',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_kubernetes_cluster_ag_map__cluster_id` FOREIGN KEY (`cluster_id`) REFERENCES `kubernetes_cluster`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kubernetes_cluster_ag_map__ag_id` FOREIGN KEY (`affinity_group_id`) REFERENCES `affinity_group`(`id`) ON DELETE CASCADE,
|
||||
INDEX `i_kubernetes_cluster_ag_map__cluster_id`(`cluster_id`),
|
||||
INDEX `i_kubernetes_cluster_ag_map__ag_id`(`affinity_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Create webhook_filter table
|
||||
DROP TABLE IF EXISTS `cloud`.`webhook_filter`;
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` (
|
||||
|
|
@ -50,6 +63,55 @@ CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` (
|
|||
CONSTRAINT `fk_webhook_filter__webhook_id` FOREIGN KEY(`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- "api_keypair" table for API and secret keys
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`api_keypair` (
|
||||
`id` bigint(20) unsigned NOT NULL auto_increment,
|
||||
`uuid` varchar(40) UNIQUE NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`domain_id` bigint(20) unsigned NOT NULL,
|
||||
`account_id` bigint(20) unsigned NOT NULL,
|
||||
`user_id` bigint(20) unsigned NOT NULL,
|
||||
`start_date` datetime,
|
||||
`end_date` datetime,
|
||||
`description` varchar(100),
|
||||
`api_key` varchar(255) NOT NULL,
|
||||
`secret_key` varchar(255) NOT NULL,
|
||||
`created` datetime NOT NULL,
|
||||
`removed` datetime,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_api_keypair__user_id` FOREIGN KEY(`user_id`) REFERENCES `cloud`.`user`(`id`),
|
||||
CONSTRAINT `fk_api_keypair__account_id` FOREIGN KEY(`account_id`) REFERENCES `cloud`.`account`(`id`),
|
||||
CONSTRAINT `fk_api_keypair__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `cloud`.`domain`(`id`)
|
||||
);
|
||||
|
||||
-- "api_keypair_permissions" table for API key pairs permissions
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`api_keypair_permissions` (
|
||||
`id` bigint(20) unsigned NOT NULL auto_increment,
|
||||
`uuid` varchar(40) UNIQUE,
|
||||
`sort_order` bigint(20) unsigned NOT NULL DEFAULT 0,
|
||||
`rule` varchar(255) NOT NULL,
|
||||
`api_keypair_id` bigint(20) unsigned NOT NULL,
|
||||
`permission` varchar(255) NOT NULL,
|
||||
`description` varchar(255),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_keypair_permissions__api_keypair_id` FOREIGN KEY(`api_keypair_id`) REFERENCES `cloud`.`api_keypair`(`id`)
|
||||
);
|
||||
|
||||
-- Populate "api_keypair" table with existing user API keys
|
||||
INSERT INTO `cloud`.`api_keypair` (uuid, user_id, domain_id, account_id, api_key, secret_key, created, name)
|
||||
SELECT UUID(), user.id, account.domain_id, account.id, user.api_key, user.secret_key, NOW(), 'Active key pair'
|
||||
FROM `cloud`.`user` AS user
|
||||
JOIN `cloud`.`account` AS account ON user.account_id = account.id
|
||||
WHERE user.api_key IS NOT NULL AND user.secret_key IS NOT NULL;
|
||||
|
||||
-- Drop API keys from user table
|
||||
ALTER TABLE `cloud`.`user` DROP COLUMN api_key, DROP COLUMN secret_key;
|
||||
|
||||
-- Grant access to the "deleteUserKeys" API to the "User", "Domain Admin" and "Resource Admin" roles, similarly to the "registerUserKeys" API
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('User', 'deleteUserKeys', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Domain Admin', 'deleteUserKeys', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'deleteUserKeys', 'ALLOW');
|
||||
|
||||
|
||||
-- ======================================================================
|
||||
-- DNS Framework Schema
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ select
|
|||
user.lastname,
|
||||
user.email,
|
||||
user.state,
|
||||
user.api_key,
|
||||
user.secret_key,
|
||||
user.created,
|
||||
user.removed,
|
||||
user.timezone,
|
||||
|
|
|
|||
|
|
@ -2565,8 +2565,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
throw new CloudRuntimeException("Destination storage pool with ID " + dataStore.getId() + " was not located.");
|
||||
}
|
||||
|
||||
boolean isSrcAndDestPoolPowerFlexStorage = srcStoragePoolVO.getPoolType().equals(Storage.StoragePoolType.PowerFlex) && destStoragePoolVO.getPoolType().equals(Storage.StoragePoolType.PowerFlex);
|
||||
if (srcStoragePoolVO.isManaged() && !isSrcAndDestPoolPowerFlexStorage && srcStoragePoolVO.getId() != destStoragePoolVO.getId()) {
|
||||
if (srcStoragePoolVO.isManaged() && srcStoragePoolVO.getId() != destStoragePoolVO.getId()) {
|
||||
throw new CloudRuntimeException("Migrating a volume online with KVM from managed storage is not currently supported.");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ public class ImageStoreHelper {
|
|||
String key = keyIter.next().toString();
|
||||
String value = details.get(key);
|
||||
// encrypt swift key or s3 secret key
|
||||
if (key.equals(ApiConstants.KEY) || key.equals(ApiConstants.S3_SECRET_KEY)) {
|
||||
if (key.equals(ApiConstants.KEY) || key.equals(ApiConstants.SECRET_KEY)) {
|
||||
value = DBEncryptionUtil.encrypt(value);
|
||||
}
|
||||
ImageStoreDetailVO detail = new ImageStoreDetailVO(store.getId(), key, value, true);
|
||||
|
|
|
|||
|
|
@ -420,6 +420,14 @@ public class ConfigKey<T> {
|
|||
return value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Still used by some external code, but use {@link ConfigKey#valueInScope(Scope, Long)} instead.
|
||||
*/
|
||||
public T valueInDomain(Long domainId) {
|
||||
return valueInScope(Scope.Domain, domainId);
|
||||
}
|
||||
|
||||
public T valueInScope(Scope scope, Long id) {
|
||||
if (id == null) {
|
||||
return value();
|
||||
|
|
|
|||
|
|
@ -17,15 +17,18 @@
|
|||
package org.apache.cloudstack.acl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity.Permission;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.utils.cache.LazyCache;
|
||||
|
|
@ -47,7 +50,7 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
|||
private RoleService roleService;
|
||||
|
||||
private List<PluggableService> services;
|
||||
private Map<RoleType, Set<String>> annotationRoleBasedApisMap = new HashMap<RoleType, Set<String>>();
|
||||
private Map<RoleType, Set<String>> annotationRoleBasedApisMap = new HashMap<>();
|
||||
|
||||
private LazyCache<Long, Account> accountCache;
|
||||
private LazyCache<Long, Pair<Role, List<RolePermission>>> rolePermissionsCache;
|
||||
|
|
@ -56,7 +59,7 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
|||
protected DynamicRoleBasedAPIAccessChecker() {
|
||||
super();
|
||||
for (RoleType roleType : RoleType.values()) {
|
||||
annotationRoleBasedApisMap.put(roleType, new HashSet<String>());
|
||||
annotationRoleBasedApisMap.put(roleType, new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,9 +70,12 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
|||
}
|
||||
|
||||
List<RolePermission> allPermissions = roleService.findAllPermissionsBy(role.getId());
|
||||
List<RolePermissionEntity> allPermissionEntities = allPermissions.stream().map(permission -> (RolePermissionEntity) permission)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> allowedApis = new ArrayList<>();
|
||||
for (String api : apiNames) {
|
||||
if (checkApiPermissionByRole(role, api, allPermissions)) {
|
||||
if (checkApiPermissionByRole(role, api, allPermissionEntities, false)) {
|
||||
allowedApis.add(api);
|
||||
}
|
||||
}
|
||||
|
|
@ -84,8 +90,8 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
|||
* @param allPermissions list of role permissions for the given role
|
||||
* @return if the role has the permission for the API
|
||||
*/
|
||||
public boolean checkApiPermissionByRole(Role role, String apiName, List<RolePermission> allPermissions) {
|
||||
for (final RolePermission permission : allPermissions) {
|
||||
public boolean checkApiPermissionByRole(Role role, String apiName, List<RolePermissionEntity> allPermissions, boolean keyPairOverride) {
|
||||
for (RolePermissionEntity permission : allPermissions) {
|
||||
if (!permission.getRule().matches(apiName)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -94,13 +100,13 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
|||
return false;
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("The API [%s] is allowed for the role %s by the permission [%s].", apiName, role, permission.getRule().toString()));
|
||||
}
|
||||
logger.trace("The API [{}] is allowed for the role {} by the permission [{}].", apiName, role, permission.getRule().toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
return annotationRoleBasedApisMap.get(role.getRoleType()) != null &&
|
||||
annotationRoleBasedApisMap.get(role.getRoleType()).contains(apiName);
|
||||
annotationRoleBasedApisMap.get(role.getRoleType()).contains(apiName) &&
|
||||
!keyPairOverride;
|
||||
}
|
||||
|
||||
protected Account getAccountFromId(long accountId) {
|
||||
|
|
@ -135,49 +141,46 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String commandName) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String commandName, ApiKeyPairPermission ... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
Account account = getAccountFromIdUsingCache(user.getAccountId());
|
||||
if (account == null) {
|
||||
throw new PermissionDeniedException(String.format("Account for user id [%s] cannot be found", user.getUuid()));
|
||||
throw new PermissionDeniedException(String.format("Account for user with ID [%s] cannot be found", user.getUuid()));
|
||||
}
|
||||
Pair<Role, List<RolePermission>> roleAndPermissions = getRolePermissionsUsingCache(account.getRoleId());
|
||||
final Role accountRole = roleAndPermissions.first();
|
||||
if (accountRole == null) {
|
||||
throw new PermissionDeniedException(String.format("Account role for user id [%s] cannot be found.", user.getUuid()));
|
||||
}
|
||||
if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId()) {
|
||||
logger.info("Account for user id {} is Root Admin or Domain Admin, all APIs are allowed.", user.getUuid());
|
||||
return true;
|
||||
}
|
||||
List<RolePermission> allPermissions = roleAndPermissions.second();
|
||||
if (checkApiPermissionByRole(accountRole, commandName, allPermissions)) {
|
||||
return true;
|
||||
}
|
||||
throw new UnavailableCommandException(String.format("The API [%s] does not exist or is not available for the account for user id [%s].", commandName, user.getUuid()));
|
||||
|
||||
return checkAccess(account, commandName, apiKeyPairPermissions);
|
||||
}
|
||||
|
||||
public boolean checkAccess(Account account, String commandName) {
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPairPermission ... apiKeyPairPermissions) {
|
||||
Pair<Role, List<RolePermission>> roleAndPermissions = getRolePermissionsUsingCache(account.getRoleId());
|
||||
final Role accountRole = roleAndPermissions.first();
|
||||
if (accountRole == null) {
|
||||
throw new PermissionDeniedException(String.format("The account [%s] has role null or unknown.", account));
|
||||
}
|
||||
|
||||
if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId()) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Account [%s] is Root Admin or Domain Admin, all APIs are allowed.", account));
|
||||
}
|
||||
if (accountRole.getRoleType() == RoleType.Admin && accountRole.getId() == RoleType.Admin.getId() && apiKeyPairPermissions.length == 0) {
|
||||
logger.info("Account [{}] is Root Admin and there aren't any API key pair permissions involved, thus, all APIs are allowed.", account);
|
||||
return true;
|
||||
}
|
||||
|
||||
List<RolePermission> allPermissions = roleService.findAllPermissionsBy(accountRole.getId());
|
||||
if (checkApiPermissionByRole(accountRole, commandName, allPermissions)) {
|
||||
boolean considerKeyPairPermissions = apiKeyPairPermissions.length > 0;
|
||||
List<RolePermissionEntity> allRules = considerKeyPairPermissions ? Arrays.asList(apiKeyPairPermissions) : new ArrayList<>(roleAndPermissions.second());
|
||||
if (checkApiPermissionByRole(accountRole, commandName, allRules, considerKeyPairPermissions)) {
|
||||
return true;
|
||||
}
|
||||
throw new UnavailableCommandException(String.format("The API [%s] does not exist or is not available for the account %s.", commandName, account));
|
||||
|
||||
throw new UnavailableCommandException(String.format("The API [%s] does not exist or is not available for the account %s.", commandName, account.getAccountName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionEntity> getImplicitRolePermissions(RoleType roleType) {
|
||||
return annotationRoleBasedApisMap.get(roleType)
|
||||
.stream()
|
||||
.map(implicitApi -> new RolePermissionBaseVO(implicitApi, Permission.ALLOW))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.UnavailableCommandException;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
@ -195,4 +197,68 @@ public class DynamicRoleBasedAPIAccessCheckerTest extends TestCase {
|
|||
List<String> apisReceived = apiAccessCheckerSpy.getApisAllowedToUser(getTestRole(), getTestUser(), apiNames);
|
||||
Assert.assertEquals(0, apisReceived.size());
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableCommandException.class)
|
||||
public void checkAccessTestInvalidApiKeyPairPermission() {
|
||||
final String api = "someDeniedApi";
|
||||
final ApiKeyPairPermission permission = new ApiKeyPairPermissionVO(1L, api, Permission.DENY, null);
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permission));
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableCommandException.class)
|
||||
public void checkAccessTestUnrelatedApiKeyPairPermission() {
|
||||
final String api = "someDeniedApi";
|
||||
final ApiKeyPairPermission permission = new ApiKeyPairPermissionVO(1L, "apiName", Permission.ALLOW, null);
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permission));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkAccessTestValidApiKeyPairPermission() {
|
||||
final String api = "someAllowedApi";
|
||||
final ApiKeyPairPermission permission = new ApiKeyPairPermissionVO(1L, api, Permission.ALLOW, null);
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permission));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkAccessTestValidMultipleApiKeyPermissions() {
|
||||
final String api = "someAllowedApi";
|
||||
final ApiKeyPairPermission[] permissions = new ApiKeyPairPermission[]{
|
||||
new ApiKeyPairPermissionVO(1L, "someDeniedApi", Permission.DENY, null),
|
||||
new ApiKeyPairPermissionVO(1L, api, Permission.ALLOW, null)
|
||||
};
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permissions));
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableCommandException.class)
|
||||
public void checkAccessTestInvalidMultipleApiKeyPermissions() {
|
||||
final String api = "someDeniedApi";
|
||||
final ApiKeyPairPermission[] permissions = new ApiKeyPairPermission[]{
|
||||
new ApiKeyPairPermissionVO(1L, "someAllowedApi", Permission.ALLOW, null),
|
||||
new ApiKeyPairPermissionVO(1L, api, Permission.DENY, null)
|
||||
};
|
||||
assertFalse(apiAccessCheckerSpy.checkAccess(getTestUser(), api, permissions));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void checkAccessTestValidApiKeyPairPermissionWithNullOverride() {
|
||||
final String api = "someAllowedApi";
|
||||
final ApiKeyPairPermission[] emptyPermissionArray = List.of().toArray(new ApiKeyPairPermission[0]);
|
||||
final RolePermission permission = new RolePermissionVO(1L, api, Permission.ALLOW, null);
|
||||
Mockito.doReturn(Collections.singletonList(permission)).when(roleServiceMock).findAllPermissionsBy(Mockito.anyLong());
|
||||
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, emptyPermissionArray));
|
||||
Mockito.verify(roleServiceMock).findAllPermissionsBy(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test(expected = UnavailableCommandException.class)
|
||||
public void checkAccessTestInvalidApiKeyPairPermissionWithNullOverride() {
|
||||
final String api = "someDeniedApi";
|
||||
final ApiKeyPairPermission[] emptyPermissionArray = List.of().toArray(new ApiKeyPairPermission[0]);
|
||||
final RolePermission permission = new RolePermissionVO(1L, api, Permission.DENY, null);
|
||||
Mockito.doReturn(Collections.singletonList(permission)).when(roleServiceMock).findAllPermissionsBy(Mockito.anyLong());
|
||||
|
||||
assertTrue(apiAccessCheckerSpy.checkAccess(getTestUser(), api, emptyPermissionArray));
|
||||
Mockito.verify(roleServiceMock, Mockito.times(1)).findAllPermissionsBy(Mockito.anyLong());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import javax.inject.Inject;
|
|||
import javax.naming.ConfigurationException;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity.Permission;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
|
|
@ -105,7 +106,7 @@ public class ProjectRoleBasedApiAccessChecker extends AdapterBase implements AP
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String apiCommandName) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -150,7 +151,7 @@ public class ProjectRoleBasedApiAccessChecker extends AdapterBase implements AP
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String apiCommandName) throws PermissionDeniedException {
|
||||
public boolean checkAccess(Account account, String apiCommandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +183,11 @@ public class ProjectRoleBasedApiAccessChecker extends AdapterBase implements AP
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionEntity> getImplicitRolePermissions(RoleType roleType) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
return super.start();
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.exception.UnavailableCommandException;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
|
||||
|
|
@ -90,7 +92,7 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String commandName) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String commandName, ApiKeyPairPermission... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -104,7 +106,7 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String commandName) {
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPairPermission... apiKeyPairPermissions) {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -163,6 +165,14 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA
|
|||
return super.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionEntity> getImplicitRolePermissions(RoleType roleType) {
|
||||
return annotationRoleBasedApisMap.get(roleType)
|
||||
.stream()
|
||||
.map(implicitApi -> new RolePermissionBaseVO(implicitApi, RolePermissionEntity.Permission.ALLOW))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void processMapping(Map<String, String> configMap) {
|
||||
for (Map.Entry<String, String> entry : configMap.entrySet()) {
|
||||
String apiName = entry.getKey();
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@
|
|||
<artifactId>cloud-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-api-limit-account-based</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class ListApisCmd extends BaseCmd {
|
|||
public void execute() throws ServerApiException {
|
||||
if (_apiDiscoveryService != null) {
|
||||
User user = CallContext.current().getCallingUser();
|
||||
ListResponse<ApiDiscoveryResponse> response = (ListResponse<ApiDiscoveryResponse>)_apiDiscoveryService.listApis(user, name);
|
||||
ListResponse<ApiDiscoveryResponse> response = (ListResponse<ApiDiscoveryResponse>)_apiDiscoveryService.listApis(user, name, this);
|
||||
if (response == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Api Discovery plugin was unable to find an api by that name or process any apis");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.cloudstack.discovery;
|
|||
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.command.user.discovery.ListApisCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
|
||||
import com.cloud.user.User;
|
||||
|
|
@ -28,5 +29,5 @@ import java.util.List;
|
|||
public interface ApiDiscoveryService extends PluggableService {
|
||||
List<String> listApiNames(Account account);
|
||||
|
||||
ListResponse<? extends BaseResponse> listApis(User user, String apiName);
|
||||
ListResponse<? extends BaseResponse> listApis(User user, String apiName, ListApisCmd cmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
|
@ -33,6 +34,8 @@ import org.apache.cloudstack.acl.APIChecker;
|
|||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairService;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
||||
|
|
@ -44,6 +47,7 @@ import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
|
|||
import org.apache.cloudstack.api.response.ApiParameterResponse;
|
||||
import org.apache.cloudstack.api.response.ApiResponseResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.ratelimit.ApiRateLimitService;
|
||||
import org.apache.cloudstack.resourcedetail.UserDetailVO;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -78,6 +82,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||
@Inject
|
||||
RoleService roleService;
|
||||
|
||||
@Inject
|
||||
ApiKeyPairService apiKeyPairService;
|
||||
|
||||
protected ApiDiscoveryServiceImpl() {
|
||||
super();
|
||||
}
|
||||
|
|
@ -257,16 +264,24 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<? extends BaseResponse> listApis(User user, String name) {
|
||||
public ListResponse<? extends BaseResponse> listApis(User user, String name, ListApisCmd cmd) {
|
||||
ListResponse<ApiDiscoveryResponse> response = new ListResponse<>();
|
||||
List<ApiDiscoveryResponse> responseList = new ArrayList<>();
|
||||
List<String> apisAllowed = new ArrayList<>(s_apiNameDiscoveryResponseMap.keySet());
|
||||
String apikey = accountService.getAccessingApiKey(cmd);
|
||||
|
||||
if (user == null)
|
||||
return null;
|
||||
Account account = accountService.getAccount(user.getAccountId());
|
||||
|
||||
if (name != null) {
|
||||
Account account = accountService.getAccount(user.getAccountId());
|
||||
if (account == null) {
|
||||
throw new PermissionDeniedException(String.format("The account with id [%s] for user [%s] is null.", user.getAccountId(), user));
|
||||
}
|
||||
|
||||
Role role = roleService.findRole(account.getRoleId());
|
||||
if (apikey != null) {
|
||||
responseList = listApisForKeyPair(apikey, name, user, role, apisAllowed);
|
||||
} else if (name != null) {
|
||||
if (!s_apiNameDiscoveryResponseMap.containsKey(name))
|
||||
return null;
|
||||
|
||||
|
|
@ -281,11 +296,6 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||
responseList.add(getApiDiscoveryResponseWithAccessibleParams(name, account));
|
||||
|
||||
} else {
|
||||
if (account == null) {
|
||||
throw new PermissionDeniedException(String.format("The account with id [%s] for user [%s] is null.", user.getAccountId(), user));
|
||||
}
|
||||
|
||||
final Role role = roleService.findRole(account.getRoleId());
|
||||
if (role == null || role.getId() < 1L) {
|
||||
throw new PermissionDeniedException(String.format("The account [%s] has role null or unknown.",
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(account, "accountName", "uuid")));
|
||||
|
|
@ -343,6 +353,44 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||
return cmdList;
|
||||
}
|
||||
|
||||
protected List<ApiDiscoveryResponse> listApisForKeyPair(String apiKey, String apiName, User user, Role role, List<String> apisAllowed) {
|
||||
List<RolePermissionEntity> keyPairPermissions = accountService.getAllKeypairPermissions(apiKey);
|
||||
|
||||
List<String> filteredApis = new ArrayList<>();
|
||||
if (apiName != null && isApiAllowedForKey(keyPairPermissions, apiName)) {
|
||||
filteredApis = List.of(apiName);
|
||||
} else {
|
||||
for (String api : apisAllowed) {
|
||||
if (isApiAllowedForKey(keyPairPermissions, api)) {
|
||||
filteredApis.add(api);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkRateLimit(user, role, filteredApis);
|
||||
return filteredApis.stream().map(api -> s_apiNameDiscoveryResponseMap.get(api)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected boolean isApiAllowedForKey(List<RolePermissionEntity> rolePermissionEntities, String apiName) {
|
||||
for (RolePermissionEntity rolePermissionEntity : rolePermissionEntities) {
|
||||
if (!rolePermissionEntity.getRule().matches(apiName)) {
|
||||
continue;
|
||||
}
|
||||
return rolePermissionEntity.getPermission().equals(RolePermissionEntity.Permission.ALLOW);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkRateLimit(User user, Role role, List<String> apiNames) {
|
||||
for (APIChecker apiChecker : _apiAccessCheckers) {
|
||||
if (!(apiChecker instanceof ApiRateLimitService)) {
|
||||
continue;
|
||||
}
|
||||
apiChecker.getApisAllowedToUser(role, user, apiNames);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public List<APIChecker> getApiAccessCheckers() {
|
||||
return _apiAccessCheckers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ public class ApiDiscoveryTest {
|
|||
@Test (expected = PermissionDeniedException.class)
|
||||
public void listApisTestThrowPermissionDeniedExceptionOnAccountNull() throws PermissionDeniedException {
|
||||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
}
|
||||
|
||||
@Test (expected = PermissionDeniedException.class)
|
||||
|
|
@ -107,7 +107,7 @@ public class ApiDiscoveryTest {
|
|||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(getNormalAccount());
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(null);
|
||||
|
||||
discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
}
|
||||
|
||||
@Test (expected = PermissionDeniedException.class)
|
||||
|
|
@ -117,7 +117,7 @@ public class ApiDiscoveryTest {
|
|||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(getNormalAccount());
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(unknownRoleVO);
|
||||
|
||||
discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -128,7 +128,7 @@ public class ApiDiscoveryTest {
|
|||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(adminAccountVO);
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(adminRoleVO);
|
||||
|
||||
discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
|
||||
Mockito.verify(apiCheckerMock, Mockito.times(0)).getApisAllowedToUser(any(Role.class), any(User.class), anyList());
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ public class ApiDiscoveryTest {
|
|||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(getNormalAccount());
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(userRoleVO);
|
||||
|
||||
discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
|
||||
Mockito.verify(apiCheckerMock, Mockito.times(1)).getApisAllowedToUser(any(Role.class), any(User.class), anyList());
|
||||
}
|
||||
|
|
@ -153,7 +153,7 @@ public class ApiDiscoveryTest {
|
|||
Mockito.when(mockUserAccount.getDetails()).thenReturn(userDetails);
|
||||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(getNormalAccount());
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(userRoleVO);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
Mockito.verify(apiCheckerMock, Mockito.times(1)).getApisAllowedToUser(any(Role.class), any(User.class), anyList());
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ public class ApiDiscoveryTest {
|
|||
Mockito.when(accountServiceMock.getAccount(Mockito.anyLong())).thenReturn(getNormalAccount());
|
||||
Mockito.when(roleServiceMock.findRole(Mockito.anyLong())).thenReturn(userRoleVO);
|
||||
Mockito.when(apiNameDiscoveryResponseMapMock.get(Mockito.anyString())).thenReturn(Mockito.mock(ApiDiscoveryResponse.class));
|
||||
ListResponse<ApiDiscoveryResponse> response = (ListResponse<ApiDiscoveryResponse>) discoveryServiceSpy.listApis(getTestUser(), null);
|
||||
ListResponse<ApiDiscoveryResponse> response = (ListResponse<ApiDiscoveryResponse>) discoveryServiceSpy.listApis(getTestUser(), null, null);
|
||||
Assert.assertEquals(4, response.getResponses().size());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ import net.sf.ehcache.Cache;
|
|||
import net.sf.ehcache.CacheManager;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -161,17 +164,17 @@ public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker,
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(User user, String apiCommandName) throws PermissionDeniedException {
|
||||
public boolean checkAccess(User user, String apiCommandName, ApiKeyPairPermission ... apiKeyPairPermissions) throws PermissionDeniedException {
|
||||
if (!isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Account account = _accountService.getAccount(user.getAccountId());
|
||||
return checkAccess(account, apiCommandName);
|
||||
return checkAccess(account, apiCommandName, apiKeyPairPermissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, String commandName) {
|
||||
public boolean checkAccess(Account account, String commandName, ApiKeyPairPermission ... apiKeyPairPermissions) {
|
||||
Long accountId = account.getAccountId();
|
||||
if (_accountService.isRootAdmin(accountId)) {
|
||||
logger.info(String.format("Account [%s] is Root Admin, in this case, API limit does not apply.",
|
||||
|
|
@ -207,6 +210,11 @@ public class ApiRateLimitServiceImpl extends AdapterBase implements APIChecker,
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionEntity> getImplicitRolePermissions(RoleType roleType) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
if (!enabled) {
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
}
|
||||
|
||||
public boolean isUserAllowedToSeeActivationRules(User user) {
|
||||
List<ApiDiscoveryResponse> apiList = (List<ApiDiscoveryResponse>) apiDiscoveryService.listApis(user, null).getResponses();
|
||||
List<ApiDiscoveryResponse> apiList = (List<ApiDiscoveryResponse>) apiDiscoveryService.listApis(user, null, null).getResponses();
|
||||
return apiList.stream().anyMatch(response -> StringUtils.equalsAny(response.getName(), "quotaTariffCreate", "quotaTariffUpdate"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
ListResponse<ApiDiscoveryResponse> responseList = new ListResponse<>();
|
||||
responseList.setResponses(cmdList);
|
||||
|
||||
Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null);
|
||||
Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null, null);
|
||||
|
||||
assertTrue(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock));
|
||||
}
|
||||
|
|
@ -668,7 +668,7 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
ListResponse<ApiDiscoveryResponse> responseList = new ListResponse<>();
|
||||
responseList.setResponses(cmdList);
|
||||
|
||||
Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null);
|
||||
Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null, null);
|
||||
|
||||
assertTrue(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock));
|
||||
}
|
||||
|
|
@ -684,7 +684,7 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
ListResponse<ApiDiscoveryResponse> responseList = new ListResponse<>();
|
||||
responseList.setResponses(cmdList);
|
||||
|
||||
Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null);
|
||||
Mockito.doReturn(responseList).when(discoveryServiceMock).listApis(userMock, null, null);
|
||||
|
||||
assertFalse(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,9 +263,8 @@ public class BaremetalVlanManagerImpl extends ManagerBase implements BaremetalVl
|
|||
user.setSource(User.Source.UNKNOWN);
|
||||
user = userDao.persist(user);
|
||||
|
||||
String[] keys = acntMgr.createApiKeyAndSecretKey(user.getId());
|
||||
user.setApiKey(keys[0]);
|
||||
user.setSecretKey(keys[1]);
|
||||
acntMgr.createApiKeyAndSecretKey(user.getId());
|
||||
|
||||
userDao.update(user.getId(), user);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -884,7 +884,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
protected StorageSubsystemCommandHandler storageHandler;
|
||||
|
||||
private boolean convertInstanceVerboseMode = false;
|
||||
private String[] convertInstanceEnv = null;
|
||||
private Map<String, String> convertInstanceEnv = null;
|
||||
protected boolean dpdkSupport = false;
|
||||
protected String dpdkOvsPath;
|
||||
protected String directDownloadTemporaryDownloadPath;
|
||||
|
|
@ -949,7 +949,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
return convertInstanceVerboseMode;
|
||||
}
|
||||
|
||||
public String[] getConvertInstanceEnv() {
|
||||
public Map<String, String> getConvertInstanceEnv() {
|
||||
return convertInstanceEnv;
|
||||
}
|
||||
|
||||
|
|
@ -1439,14 +1439,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
return;
|
||||
}
|
||||
if (StringUtils.isNotBlank(convertEnvTmpDir) && StringUtils.isNotBlank(convertEnvVirtv2vTmpDir)) {
|
||||
convertInstanceEnv = new String[2];
|
||||
convertInstanceEnv[0] = String.format("%s=%s", "TMPDIR", convertEnvTmpDir);
|
||||
convertInstanceEnv[1] = String.format("%s=%s", "VIRT_V2V_TMPDIR", convertEnvVirtv2vTmpDir);
|
||||
convertInstanceEnv = new HashMap<>(2);
|
||||
convertInstanceEnv.put("TMPDIR", convertEnvTmpDir);
|
||||
convertInstanceEnv.put("VIRT_V2V_TMPDIR", convertEnvVirtv2vTmpDir);
|
||||
} else {
|
||||
convertInstanceEnv = new String[1];
|
||||
convertInstanceEnv = new HashMap<>(1);
|
||||
String key = StringUtils.isNotBlank(convertEnvTmpDir) ? "TMPDIR" : "VIRT_V2V_TMPDIR";
|
||||
String value = StringUtils.isNotBlank(convertEnvTmpDir) ? convertEnvTmpDir : convertEnvVirtv2vTmpDir;
|
||||
convertInstanceEnv[0] = String.format("%s=%s", key, value);
|
||||
convertInstanceEnv.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@ import java.net.URLEncoder;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
|
@ -244,7 +246,12 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper<Convert
|
|||
|
||||
String logPrefix = String.format("(%s) virt-v2v ovf source: %s progress", originalVMName, sourceOVFDirPath);
|
||||
OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(logger, logPrefix);
|
||||
script.execute(outputLogger);
|
||||
Map<String, String> convertInstanceEnv = serverResource.getConvertInstanceEnv();
|
||||
if (MapUtils.isEmpty(convertInstanceEnv)) {
|
||||
script.execute(outputLogger);
|
||||
} else {
|
||||
script.execute(outputLogger, convertInstanceEnv);
|
||||
}
|
||||
int exitValue = script.getExitValue();
|
||||
return exitValue == 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,8 @@ public class ScaleIOStorageAdaptorTest {
|
|||
details.put(ScaleIOGatewayClient.STORAGE_POOL_MDMS, "1.1.1.1,2.2.2.2");
|
||||
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl status scini"))).thenReturn(3);
|
||||
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0);
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"))).thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-3.3.3.3 [1]-4.4.4.4");
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
|
||||
.thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-3.3.3.3 [1]-4.4.4.4");
|
||||
|
||||
Pair<Boolean, String> result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details);
|
||||
|
||||
|
|
@ -196,11 +197,11 @@ public class ScaleIOStorageAdaptorTest {
|
|||
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl is-enabled scini"))).thenReturn(0);
|
||||
when(Script.executeCommand(Mockito.eq("sed -i '/1.1.1.1\\,/d' /etc/emc/scaleio/drv_cfg.txt"))).thenReturn(new Pair<>(null, null));
|
||||
when(Script.runSimpleBashScriptForExitValue(Mockito.eq("systemctl restart scini"))).thenReturn(0);
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 1.1.1.1"))).thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-1.1.1.1 [1]-2.2.2.2");
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms --file /etc/emc/scaleio/drv_cfg.txt|grep 1.1.1.1"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
|
||||
.thenReturn("MDM-ID 71fd458f0775010f SDC ID 4421a91a00000000 INSTALLATION ID 204930df2cbcaf8e IPs [0]-1.1.1.1 [1]-2.2.2.2");
|
||||
when(Script.executeCommand(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg"))).thenReturn(new Pair<>(null, null));
|
||||
when(Script.executeCommand(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_vols"))).thenReturn(new Pair<>("", null));
|
||||
|
||||
|
||||
Pair<Boolean, String> result = scaleIOStorageAdaptor.unprepareStorageClient(poolUuid, details);
|
||||
|
||||
Assert.assertFalse(result.first());
|
||||
|
|
|
|||
|
|
@ -95,9 +95,8 @@ public class ScaleIOStoragePoolTest {
|
|||
details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId);
|
||||
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
when(Script.runSimpleBashScript(
|
||||
"/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 218ce1797566a00f|awk '{print $5}'")).thenReturn(
|
||||
sdcId);
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms --file /etc/emc/scaleio/drv_cfg.txt|grep 218ce1797566a00f|awk '{print $5}'"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
|
||||
.thenReturn(sdcId);
|
||||
|
||||
ScaleIOStoragePool pool1 = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type,
|
||||
details, adapter);
|
||||
|
|
@ -116,10 +115,10 @@ public class ScaleIOStoragePoolTest {
|
|||
details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId);
|
||||
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
when(Script.runSimpleBashScript(
|
||||
"/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 218ce1797566a00f|awk '{print $5}'")).thenReturn(
|
||||
null);
|
||||
when(Script.runSimpleBashScript("/opt/emc/scaleio/sdc/bin/drv_cfg --query_guid")).thenReturn(sdcGuid);
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms --file /etc/emc/scaleio/drv_cfg.txt|grep 218ce1797566a00f|awk '{print $5}'"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
|
||||
.thenReturn(null);
|
||||
when(Script.runSimpleBashScript(Mockito.eq("/opt/emc/scaleio/sdc/bin/drv_cfg --query_guid --file /etc/emc/scaleio/drv_cfg.txt"), Mockito.eq(ScaleIOUtil.DEFAULT_TIMEOUT_MS)))
|
||||
.thenReturn(sdcGuid);
|
||||
|
||||
ScaleIOStoragePool pool1 = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type,
|
||||
details, adapter);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
// 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 com.cloud.kubernetes.cluster;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
@Entity
|
||||
@Table(name = "kubernetes_cluster_affinity_group_map")
|
||||
public class KubernetesClusterAffinityGroupMapVO implements InternalIdentity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "cluster_id")
|
||||
private long clusterId;
|
||||
|
||||
@Column(name = "node_type")
|
||||
private String nodeType;
|
||||
|
||||
@Column(name = "affinity_group_id")
|
||||
private long affinityGroupId;
|
||||
|
||||
public KubernetesClusterAffinityGroupMapVO() {
|
||||
}
|
||||
|
||||
public KubernetesClusterAffinityGroupMapVO(long clusterId, String nodeType, long affinityGroupId) {
|
||||
this.clusterId = clusterId;
|
||||
this.nodeType = nodeType;
|
||||
this.affinityGroupId = affinityGroupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getClusterId() {
|
||||
return clusterId;
|
||||
}
|
||||
|
||||
public void setClusterId(long clusterId) {
|
||||
this.clusterId = clusterId;
|
||||
}
|
||||
|
||||
public String getNodeType() {
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
public void setNodeType(String nodeType) {
|
||||
this.nodeType = nodeType;
|
||||
}
|
||||
|
||||
public long getAffinityGroupId() {
|
||||
return affinityGroupId;
|
||||
}
|
||||
|
||||
public void setAffinityGroupId(long affinityGroupId) {
|
||||
this.affinityGroupId = affinityGroupId;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,4 +25,5 @@ public class KubernetesClusterEventTypes {
|
|||
public static final String EVENT_KUBERNETES_CLUSTER_UPGRADE = "KUBERNETES.CLUSTER.UPGRADE";
|
||||
public static final String EVENT_KUBERNETES_CLUSTER_NODES_ADD = "KUBERNETES.CLUSTER.NODES.ADD";
|
||||
public static final String EVENT_KUBERNETES_CLUSTER_NODES_REMOVE = "KUBERNETES.CLUSTER.NODES.REMOVE";
|
||||
public static final String EVENT_KUBERNETES_CLUSTER_AFFINITY_UPDATE = "KUBERNETES.CLUSTER.AFFINITY.UPDATE";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import javax.naming.ConfigurationException;
|
|||
|
||||
import com.cloud.configuration.Resource;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairVO;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
|
|
@ -57,7 +58,9 @@ import org.apache.cloudstack.acl.RoleType;
|
|||
import org.apache.cloudstack.acl.Rule;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.AffinityProcessorBase;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
|
|
@ -86,6 +89,7 @@ import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMa
|
|||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpdateKubernetesClusterAffinityGroupCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.loadbalancer.AssignToLoadBalancerRuleCmd;
|
||||
import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerRuleCmd;
|
||||
|
|
@ -169,6 +173,7 @@ import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterScaleWorker;
|
|||
import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterStartWorker;
|
||||
import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterStopWorker;
|
||||
import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterUpgradeWorker;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterAffinityGroupMapDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDetailsDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||
|
|
@ -315,6 +320,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
@Inject
|
||||
public KubernetesClusterDetailsDao kubernetesClusterDetailsDao;
|
||||
@Inject
|
||||
public KubernetesClusterAffinityGroupMapDao kubernetesClusterAffinityGroupMapDao;
|
||||
@Inject
|
||||
public KubernetesSupportedVersionDao kubernetesSupportedVersionDao;
|
||||
@Inject
|
||||
protected SSHKeyPairDao sshKeyPairDao;
|
||||
|
|
@ -329,6 +336,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
@Inject
|
||||
protected AffinityGroupDao affinityGroupDao;
|
||||
@Inject
|
||||
protected AffinityGroupVMMapDao affinityGroupVMMapDao;
|
||||
@Inject
|
||||
protected ServiceOfferingDao serviceOfferingDao;
|
||||
@Inject
|
||||
protected UserDataDao userDataDao;
|
||||
|
|
@ -698,8 +707,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
} else if (Objects.nonNull(domainId)) {
|
||||
dedicatedHosts = dedicatedResourceDao.listByDomainId(domainId);
|
||||
}
|
||||
for (DedicatedResourceVO dedicatedHost : dedicatedHosts) {
|
||||
hosts.add(hostDao.findById(dedicatedHost.getHostId()));
|
||||
for (DedicatedResourceVO dedicatedResource : dedicatedHosts) {
|
||||
hosts.addAll(getHostsForDedicatedResource(dedicatedResource, zone));
|
||||
useDedicatedHosts = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -767,6 +776,23 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
throw new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId());
|
||||
}
|
||||
|
||||
public List<HostVO> getHostsForDedicatedResource(DedicatedResourceVO dedicatedResource, DataCenter zone) {
|
||||
if (dedicatedResource.getHostId() != null) {
|
||||
HostVO host = hostDao.findById(dedicatedResource.getHostId());
|
||||
return host != null ? List.of(host) : Collections.emptyList();
|
||||
}
|
||||
if (dedicatedResource.getClusterId() != null) {
|
||||
return hostDao.findByClusterId(dedicatedResource.getClusterId());
|
||||
}
|
||||
if (dedicatedResource.getPodId() != null) {
|
||||
return hostDao.findByPodId(dedicatedResource.getPodId(), Host.Type.Routing);
|
||||
}
|
||||
if (dedicatedResource.getDataCenterId() != null) {
|
||||
return resourceManager.listAllHostsInOneZoneByType(Host.Type.Routing, dedicatedResource.getDataCenterId());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected void setNodeTypeServiceOfferingResponse(KubernetesClusterResponse response,
|
||||
KubernetesClusterNodeType nodeType,
|
||||
Long offeringId) {
|
||||
|
|
@ -858,24 +884,38 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
|
||||
List<KubernetesUserVmResponse> vmResponses = new ArrayList<>();
|
||||
List<KubernetesClusterVmMapVO> vmList = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
|
||||
ResponseView respView = ResponseView.Restricted;
|
||||
ResponseView userVmResponseView = ResponseView.Restricted;
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
if (accountService.isRootAdmin(caller.getId())) {
|
||||
respView = ResponseView.Full;
|
||||
userVmResponseView = ResponseView.Full;
|
||||
}
|
||||
final String responseName = "virtualmachine";
|
||||
if (vmList != null && !vmList.isEmpty()) {
|
||||
for (KubernetesClusterVmMapVO vmMapVO : vmList) {
|
||||
UserVmJoinVO userVM = userVmJoinDao.findById(vmMapVO.getVmId());
|
||||
if (userVM != null) {
|
||||
UserVmResponse vmResponse = ApiDBUtils.newUserVmResponse(respView, responseName, userVM,
|
||||
EnumSet.of(VMDetails.nics), caller);
|
||||
Map<Long, KubernetesClusterVmMapVO> vmMapById = vmList.stream()
|
||||
.collect(Collectors.toMap(KubernetesClusterVmMapVO::getVmId, vm -> vm));
|
||||
Long[] vmIds = vmMapById.keySet().toArray(new Long[0]);
|
||||
List<UserVmJoinVO> userVmJoinVOs = userVmJoinDao.searchByIds(vmIds);
|
||||
if (userVmJoinVOs != null && !userVmJoinVOs.isEmpty()) {
|
||||
Map<Long, UserVmResponse> vmResponseMap = new HashMap<>();
|
||||
for (UserVmJoinVO userVM : userVmJoinVOs) {
|
||||
Long vmId = userVM.getId();
|
||||
UserVmResponse vmResponse = vmResponseMap.get(vmId);
|
||||
if (vmResponse == null) {
|
||||
vmResponse = ApiDBUtils.newUserVmResponse(userVmResponseView, responseName, userVM,
|
||||
EnumSet.of(VMDetails.nics, VMDetails.affgrp), caller);
|
||||
vmResponseMap.put(vmId, vmResponse);
|
||||
} else {
|
||||
ApiDBUtils.fillVmDetails(userVmResponseView, vmResponse, userVM);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<Long, UserVmResponse> vmIdResponseEntry : vmResponseMap.entrySet()) {
|
||||
KubernetesUserVmResponse kubernetesUserVmResponse = new KubernetesUserVmResponse();
|
||||
try {
|
||||
BeanUtils.copyProperties(kubernetesUserVmResponse, vmResponse);
|
||||
BeanUtils.copyProperties(kubernetesUserVmResponse, vmIdResponseEntry.getValue());
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate zone metrics response");
|
||||
}
|
||||
KubernetesClusterVmMapVO vmMapVO = vmMapById.get(vmIdResponseEntry.getKey());
|
||||
kubernetesUserVmResponse.setExternalNode(vmMapVO.isExternalNode());
|
||||
kubernetesUserVmResponse.setEtcdNode(vmMapVO.isEtcdNode());
|
||||
kubernetesUserVmResponse.setNodeVersion(vmMapVO.getNodeVersion());
|
||||
|
|
@ -905,10 +945,45 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
response.setClusterType(kubernetesCluster.getClusterType());
|
||||
response.setCsiEnabled(kubernetesCluster.isCsiEnabled());
|
||||
response.setCreated(kubernetesCluster.getCreated());
|
||||
setNodeTypeAffinityGroupResponse(response, kubernetesCluster.getId());
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected void setNodeTypeAffinityGroupResponse(KubernetesClusterResponse response, long clusterId) {
|
||||
setAffinityGroupResponseForNodeType(response, clusterId, CONTROL.name());
|
||||
setAffinityGroupResponseForNodeType(response, clusterId, WORKER.name());
|
||||
setAffinityGroupResponseForNodeType(response, clusterId, ETCD.name());
|
||||
}
|
||||
|
||||
protected void setAffinityGroupResponseForNodeType(KubernetesClusterResponse response, long clusterId, String nodeType) {
|
||||
List<Long> affinityGroupIds = kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, nodeType);
|
||||
if (CollectionUtils.isEmpty(affinityGroupIds)) {
|
||||
return;
|
||||
}
|
||||
List<String> affinityGroupUuids = new ArrayList<>();
|
||||
List<String> affinityGroupNames = new ArrayList<>();
|
||||
for (Long affinityGroupId : affinityGroupIds) {
|
||||
AffinityGroupVO affinityGroup = affinityGroupDao.findById(affinityGroupId);
|
||||
if (affinityGroup != null) {
|
||||
affinityGroupUuids.add(affinityGroup.getUuid());
|
||||
affinityGroupNames.add(affinityGroup.getName());
|
||||
}
|
||||
}
|
||||
String affinityGroupUuidsCsv = String.join(",", affinityGroupUuids);
|
||||
String affinityGroupNamesCsv = String.join(",", affinityGroupNames);
|
||||
if (CONTROL.name().equals(nodeType)) {
|
||||
response.setControlAffinityGroupIds(affinityGroupUuidsCsv);
|
||||
response.setControlAffinityGroupNames(affinityGroupNamesCsv);
|
||||
} else if (WORKER.name().equals(nodeType)) {
|
||||
response.setWorkerAffinityGroupIds(affinityGroupUuidsCsv);
|
||||
response.setWorkerAffinityGroupNames(affinityGroupNamesCsv);
|
||||
} else if (ETCD.name().equals(nodeType)) {
|
||||
response.setEtcdAffinityGroupIds(affinityGroupUuidsCsv);
|
||||
response.setEtcdAffinityGroupNames(affinityGroupNamesCsv);
|
||||
}
|
||||
}
|
||||
|
||||
private DataCenter validateAndGetZoneForKubernetesCreateParameters(Long zoneId, Long networkId) {
|
||||
DataCenter zone = dataCenterDao.findById(zoneId);
|
||||
if (zone == null) {
|
||||
|
|
@ -1190,6 +1265,20 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
return network;
|
||||
}
|
||||
|
||||
private void persistAffinityGroupMappings(long clusterId, Map<String, List<Long>> affinityGroupNodeTypeMap) {
|
||||
if (MapUtils.isEmpty(affinityGroupNodeTypeMap)) {
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, List<Long>> nodeTypeAffinityGroupEntry : affinityGroupNodeTypeMap.entrySet()) {
|
||||
String nodeType = nodeTypeAffinityGroupEntry.getKey();
|
||||
List<Long> affinityGroupIds = nodeTypeAffinityGroupEntry.getValue();
|
||||
for (Long affinityGroupId : affinityGroupIds) {
|
||||
kubernetesClusterAffinityGroupMapDao.persist(
|
||||
new KubernetesClusterAffinityGroupMapVO(clusterId, nodeType, affinityGroupId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addKubernetesClusterDetails(final KubernetesCluster kubernetesCluster, final Network network, final CreateKubernetesClusterCmd cmd) {
|
||||
final String externalLoadBalancerIpAddress = cmd.getExternalLoadBalancerIpAddress();
|
||||
final String dockerRegistryUserName = cmd.getDockerRegistryUserName();
|
||||
|
|
@ -1629,6 +1718,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
Map<String, Long> templateNodeTypeMap = cmd.getTemplateNodeTypeMap();
|
||||
Map<String, List<Long>> affinityGroupNodeTypeMap = cmd.getAffinityGroupNodeTypeMap();
|
||||
final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, DEFAULT, clusterKubernetesVersion);
|
||||
final VMTemplateVO controlNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, CONTROL, clusterKubernetesVersion);
|
||||
final VMTemplateVO workerNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, WORKER, clusterKubernetesVersion);
|
||||
|
|
@ -1674,6 +1764,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
newCluster.setCsiEnabled(cmd.getEnableCsi());
|
||||
kubernetesClusterDao.persist(newCluster);
|
||||
persistAffinityGroupMappings(newCluster.getId(), affinityGroupNodeTypeMap);
|
||||
addKubernetesClusterDetails(newCluster, defaultNetwork, cmd);
|
||||
return newCluster;
|
||||
}
|
||||
|
|
@ -1890,12 +1981,12 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
KUBEADMIN_ACCOUNT_NAME, "kubeadmin", null, UUID.randomUUID().toString(), User.Source.UNKNOWN));
|
||||
keys = createUserApiKeyAndSecretKey(kube.getId());
|
||||
} else {
|
||||
String apiKey = kubeadmin.getApiKey();
|
||||
String secretKey = kubeadmin.getSecretKey();
|
||||
if (StringUtils.isAnyEmpty(apiKey, secretKey)) {
|
||||
ApiKeyPairVO latestKeypair = ApiDBUtils.searchForLatestUserKeyPair(kubeadmin.getId());
|
||||
|
||||
if (latestKeypair == null) {
|
||||
keys = createUserApiKeyAndSecretKey(kubeadmin.getId());
|
||||
} else {
|
||||
keys = new String[]{apiKey, secretKey};
|
||||
keys = new String[]{latestKeypair.getApiKey(), latestKeypair.getSecretKey()};
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
|
|
@ -2230,6 +2321,94 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
return upgradeWorker.upgradeCluster();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = KubernetesClusterEventTypes.EVENT_KUBERNETES_CLUSTER_AFFINITY_UPDATE,
|
||||
eventDescription = "updating Kubernetes cluster affinity groups")
|
||||
public boolean updateKubernetesClusterAffinityGroups(UpdateKubernetesClusterAffinityGroupCmd cmd) throws CloudRuntimeException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
}
|
||||
KubernetesClusterVO kubernetesCluster = validateClusterForAffinityGroupUpdate(cmd.getId());
|
||||
Map<String, List<Long>> affinityGroupNodeTypeMap = cmd.getAffinityGroupNodeTypeMap();
|
||||
validateNodeAffinityGroups(affinityGroupNodeTypeMap, kubernetesCluster.getAccountId());
|
||||
|
||||
final Long clusterId = kubernetesCluster.getId();
|
||||
Transaction.execute(new TransactionCallbackNoReturn() {
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
kubernetesClusterAffinityGroupMapDao.removeByClusterId(clusterId);
|
||||
persistAffinityGroupMappings(clusterId, affinityGroupNodeTypeMap);
|
||||
syncVmAffinityGroups(clusterId, affinityGroupNodeTypeMap);
|
||||
}
|
||||
});
|
||||
logger.info("Updated affinity groups for Kubernetes cluster {}", kubernetesCluster.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
private KubernetesClusterVO validateClusterForAffinityGroupUpdate(Long clusterId) {
|
||||
KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(clusterId);
|
||||
if (Objects.isNull(kubernetesCluster) || Objects.nonNull(kubernetesCluster.getRemoved())) {
|
||||
throw new InvalidParameterValueException("Invalid Kubernetes cluster ID");
|
||||
}
|
||||
if (!KubernetesCluster.ClusterType.CloudManaged.equals(kubernetesCluster.getClusterType())) {
|
||||
throw new InvalidParameterValueException("Affinity groups can only be updated for CloudManaged Kubernetes clusters");
|
||||
}
|
||||
if (!KubernetesCluster.State.Stopped.equals(kubernetesCluster.getState())) {
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Kubernetes cluster %s must be stopped before updating affinity groups (current state: %s)",
|
||||
kubernetesCluster.getName(), kubernetesCluster.getState()));
|
||||
}
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(),
|
||||
SecurityChecker.AccessType.OperateEntry, false, kubernetesCluster);
|
||||
return kubernetesCluster;
|
||||
}
|
||||
|
||||
private void validateNodeAffinityGroups(Map<String, List<Long>> affinityGroupNodeTypeMap, long ownerAccountId) {
|
||||
if (MapUtils.isEmpty(affinityGroupNodeTypeMap)) {
|
||||
return;
|
||||
}
|
||||
Account owner = accountDao.findById(ownerAccountId);
|
||||
for (List<Long> affinityGroupIds : affinityGroupNodeTypeMap.values()) {
|
||||
for (Long affinityGroupId : affinityGroupIds) {
|
||||
AffinityGroupVO affinityGroup = affinityGroupDao.findById(affinityGroupId);
|
||||
if (Objects.isNull(affinityGroup)) {
|
||||
throw new InvalidParameterValueException("Unable to find affinity group with ID: " + affinityGroupId);
|
||||
}
|
||||
if (affinityGroup.getAccountId() != owner.getAccountId()) {
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Affinity group %s does not belong to the cluster owner account %s",
|
||||
affinityGroup.getName(), owner.getAccountName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void syncVmAffinityGroups(Long clusterId, Map<String, List<Long>> affinityGroupNodeTypeMap) {
|
||||
List<KubernetesClusterVmMapVO> clusterVmMappings = kubernetesClusterVmMapDao.listByClusterId(clusterId);
|
||||
if (CollectionUtils.isEmpty(clusterVmMappings)) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<Long>> nodeTypeAffinityMap = MapUtils.isEmpty(affinityGroupNodeTypeMap)
|
||||
? Collections.emptyMap() : affinityGroupNodeTypeMap;
|
||||
for (KubernetesClusterVmMapVO clusterVmMapping : clusterVmMappings) {
|
||||
if (clusterVmMapping.isExternalNode()) {
|
||||
continue;
|
||||
}
|
||||
String nodeType = getNodeType(clusterVmMapping);
|
||||
affinityGroupVMMapDao.updateMap(clusterVmMapping.getVmId(),
|
||||
nodeTypeAffinityMap.getOrDefault(nodeType, Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
|
||||
private String getNodeType(KubernetesClusterVmMapVO clusterVmMapping) {
|
||||
if (clusterVmMapping.isControlNode()) {
|
||||
return CONTROL.name();
|
||||
} else if (clusterVmMapping.isEtcdNode()) {
|
||||
return ETCD.name();
|
||||
}
|
||||
return WORKER.name();
|
||||
}
|
||||
|
||||
private void updateNodeCount(KubernetesClusterVO kubernetesCluster) {
|
||||
List<KubernetesClusterVmMapVO> nodeList = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
|
||||
kubernetesCluster.setControlNodeCount(nodeList.stream().filter(KubernetesClusterVmMapVO::isControlNode).count());
|
||||
|
|
@ -2291,6 +2470,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
if (validNodeIds.isEmpty()) {
|
||||
throw new CloudRuntimeException("No valid nodes found to be added to the Kubernetes cluster");
|
||||
}
|
||||
validateNodeAffinityGroups(validNodeIds, kubernetesCluster);
|
||||
KubernetesClusterAddWorker addWorker = new KubernetesClusterAddWorker(kubernetesCluster, KubernetesClusterManagerImpl.this);
|
||||
addWorker = ComponentContext.inject(addWorker);
|
||||
return addWorker.addNodesToCluster(validNodeIds, cmd.isMountCksIsoOnVr(), cmd.isManualUpgrade());
|
||||
|
|
@ -2350,6 +2530,98 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
return validNodeIds;
|
||||
}
|
||||
|
||||
protected void validateNodeAffinityGroups(List<Long> nodeIds, KubernetesCluster cluster) {
|
||||
List<Long> workerAffinityGroupIds = kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(
|
||||
cluster.getId(), WORKER.name());
|
||||
if (CollectionUtils.isEmpty(workerAffinityGroupIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Long> existingWorkerHostIds = getExistingWorkerHostIds(cluster);
|
||||
|
||||
for (Long affinityGroupId : workerAffinityGroupIds) {
|
||||
AffinityGroupVO affinityGroup = affinityGroupDao.findById(affinityGroupId);
|
||||
if (affinityGroup == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
validateNodesAgainstExistingWorkers(nodeIds, existingWorkerHostIds, affinityGroup, cluster);
|
||||
validateNewNodesAntiAffinity(nodeIds, affinityGroup, cluster);
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<Long> getExistingWorkerHostIds(KubernetesCluster cluster) {
|
||||
List<KubernetesClusterVmMapVO> existingWorkerVms = kubernetesClusterVmMapDao.listByClusterIdAndVmType(cluster.getId(), WORKER);
|
||||
Set<Long> existingWorkerHostIds = new HashSet<>();
|
||||
for (KubernetesClusterVmMapVO workerVmMap : existingWorkerVms) {
|
||||
VMInstanceVO workerVm = vmInstanceDao.findById(workerVmMap.getVmId());
|
||||
if (workerVm != null && workerVm.getHostId() != null) {
|
||||
existingWorkerHostIds.add(workerVm.getHostId());
|
||||
}
|
||||
}
|
||||
return existingWorkerHostIds;
|
||||
}
|
||||
|
||||
protected void validateNodesAgainstExistingWorkers(List<Long> nodeIds, Set<Long> existingWorkerHostIds,
|
||||
AffinityGroupVO affinityGroup, KubernetesCluster cluster) {
|
||||
for (Long nodeId : nodeIds) {
|
||||
VMInstanceVO node = vmInstanceDao.findById(nodeId);
|
||||
if (node == null || node.getHostId() == null) {
|
||||
continue;
|
||||
}
|
||||
Long nodeHostId = node.getHostId();
|
||||
String nodeHostName = getHostName(nodeHostId);
|
||||
|
||||
if (AffinityProcessorBase.AFFINITY_TYPE_HOST_ANTI.equals(affinityGroup.getType())) {
|
||||
if (existingWorkerHostIds.contains(nodeHostId)) {
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Cannot add VM %s to cluster %s. VM is running on host %s which violates the cluster's " +
|
||||
"host anti-affinity rule (affinity group: %s). Existing worker VMs are already running on this host.",
|
||||
node.getInstanceName(), cluster.getName(), nodeHostName, affinityGroup.getName()));
|
||||
}
|
||||
} else if (AffinityProcessorBase.AFFINITY_TYPE_HOST.equals(affinityGroup.getType())) {
|
||||
if (!existingWorkerHostIds.isEmpty() && !existingWorkerHostIds.contains(nodeHostId)) {
|
||||
List<String> existingHostNames = existingWorkerHostIds.stream()
|
||||
.map(this::getHostName)
|
||||
.collect(Collectors.toList());
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Cannot add VM %s to cluster %s. VM is running on host %s which violates the cluster's " +
|
||||
"host affinity rule (affinity group: %s). All worker VMs must run on the same host. " +
|
||||
"Existing workers are on host(s): %s.",
|
||||
node.getInstanceName(), cluster.getName(), nodeHostName, affinityGroup.getName(),
|
||||
String.join(", ", existingHostNames)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateNewNodesAntiAffinity(List<Long> nodeIds, AffinityGroupVO affinityGroup, KubernetesCluster cluster) {
|
||||
if (!AffinityProcessorBase.AFFINITY_TYPE_HOST_ANTI.equals(affinityGroup.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Long> newNodeHostIds = new HashSet<>();
|
||||
for (Long nodeId : nodeIds) {
|
||||
VMInstanceVO node = vmInstanceDao.findById(nodeId);
|
||||
if (node != null && node.getHostId() != null) {
|
||||
Long nodeHostId = node.getHostId();
|
||||
if (newNodeHostIds.contains(nodeHostId)) {
|
||||
String nodeHostName = getHostName(nodeHostId);
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Cannot add VM %s to cluster %s. Multiple VMs being added are running on the same host %s, " +
|
||||
"which violates the cluster's host anti-affinity rule (affinity group: %s).",
|
||||
node.getInstanceName(), cluster.getName(), nodeHostName, affinityGroup.getName()));
|
||||
}
|
||||
newNodeHostIds.add(nodeHostId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String getHostName(Long hostId) {
|
||||
HostVO host = hostDao.findById(hostId);
|
||||
return host != null ? host.getName() : String.valueOf(hostId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RemoveVirtualMachinesFromKubernetesClusterResponse> removeVmsFromCluster(RemoveVirtualMachinesFromKubernetesClusterCmd cmd) {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
|
|
@ -2486,6 +2758,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
cmdList.add(RemoveVirtualMachinesFromKubernetesClusterCmd.class);
|
||||
cmdList.add(AddNodesToKubernetesClusterCmd.class);
|
||||
cmdList.add(RemoveNodesFromKubernetesClusterCmd.class);
|
||||
cmdList.add(UpdateKubernetesClusterAffinityGroupCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMa
|
|||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpdateKubernetesClusterAffinityGroupCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse;
|
||||
import org.apache.cloudstack.api.response.KubernetesClusterResponse;
|
||||
|
|
@ -171,6 +172,8 @@ public interface KubernetesClusterService extends PluggableService, Configurable
|
|||
|
||||
boolean upgradeKubernetesCluster(UpgradeKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
boolean updateKubernetesClusterAffinityGroups(UpdateKubernetesClusterAffinityGroupCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
boolean addVmsToCluster(AddVirtualMachinesToKubernetesClusterCmd cmd);
|
||||
|
||||
boolean addNodesToKubernetesCluster(AddNodesToKubernetesClusterCmd cmd);
|
||||
|
|
|
|||
|
|
@ -18,12 +18,16 @@ package com.cloud.kubernetes.cluster;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
|
|
@ -66,6 +70,8 @@ public class KubernetesServiceHelperImpl extends AdapterBase implements Kubernet
|
|||
@Inject
|
||||
protected VMTemplateDao vmTemplateDao;
|
||||
@Inject
|
||||
protected AffinityGroupDao affinityGroupDao;
|
||||
@Inject
|
||||
KubernetesClusterService kubernetesClusterService;
|
||||
|
||||
protected void setEventTypeEntityDetails(Class<?> eventTypeDefinedClass, Class<?> entityClass) {
|
||||
|
|
@ -123,6 +129,27 @@ public class KubernetesServiceHelperImpl extends AdapterBase implements Kubernet
|
|||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkVmAffinityGroupsCanBeUpdated(UserVm userVm) {
|
||||
if (!UserVmManager.CKS_NODE.equals(userVm.getUserVmType())) {
|
||||
return;
|
||||
}
|
||||
KubernetesClusterVmMapVO clusterVmMapping = kubernetesClusterVmMapDao.findByVmId(userVm.getId());
|
||||
if (Objects.isNull(clusterVmMapping)) {
|
||||
return;
|
||||
}
|
||||
KubernetesCluster kubernetesCluster = kubernetesClusterDao.findById(clusterVmMapping.getClusterId());
|
||||
String errorMessage = "Affinity groups cannot be updated for a VM part of Kubernetes cluster";
|
||||
if (Objects.nonNull(kubernetesCluster)) {
|
||||
if (KubernetesCluster.ClusterType.ExternalManaged.equals(kubernetesCluster.getClusterType())) {
|
||||
return;
|
||||
}
|
||||
errorMessage += String.format(": %s", kubernetesCluster.getName());
|
||||
}
|
||||
errorMessage += ". Please use the cluster's Change Affinity option instead.";
|
||||
throw new CloudRuntimeException(errorMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidNodeType(String nodeType) {
|
||||
if (StringUtils.isBlank(nodeType)) {
|
||||
|
|
@ -244,6 +271,81 @@ public class KubernetesServiceHelperImpl extends AdapterBase implements Kubernet
|
|||
return mapping;
|
||||
}
|
||||
|
||||
protected void checkNodeTypeAffinityGroupEntryCompleteness(String nodeType, String affinityGroupUuids) {
|
||||
if (StringUtils.isAnyBlank(nodeType, affinityGroupUuids)) {
|
||||
String error = String.format("Any Node Type to Affinity Group entry should have valid '%s' and '%s' values",
|
||||
VmDetailConstants.CKS_NODE_TYPE, VmDetailConstants.AFFINITY_GROUP);
|
||||
logger.error(error);
|
||||
throw new InvalidParameterValueException(error);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkNodeTypeAffinityGroupEntryNodeType(String nodeType) {
|
||||
if (!isValidNodeType(nodeType)) {
|
||||
String error = String.format("The provided value '%s' for Node Type is invalid", nodeType);
|
||||
logger.error(error);
|
||||
throw new InvalidParameterValueException(error);
|
||||
}
|
||||
}
|
||||
|
||||
protected Long validateAffinityGroupUuidAndGetId(String affinityGroupUuid) {
|
||||
if (StringUtils.isBlank(affinityGroupUuid)) {
|
||||
String error = "Empty affinity group UUID provided";
|
||||
logger.error(error);
|
||||
throw new InvalidParameterValueException(error);
|
||||
}
|
||||
AffinityGroup affinityGroup = affinityGroupDao.findByUuid(affinityGroupUuid);
|
||||
if (affinityGroup == null) {
|
||||
String error = String.format("Cannot find an affinity group with ID %s", affinityGroupUuid);
|
||||
logger.error(error);
|
||||
throw new InvalidParameterValueException(error);
|
||||
}
|
||||
return affinityGroup.getId();
|
||||
}
|
||||
|
||||
protected List<Long> validateAndGetAffinityGroupIds(String affinityGroupUuids) {
|
||||
String[] uuids = affinityGroupUuids.split(",");
|
||||
List<Long> affinityGroupIds = new ArrayList<>();
|
||||
for (String uuid : uuids) {
|
||||
String trimmedUuid = uuid.trim();
|
||||
Long affinityGroupId = validateAffinityGroupUuidAndGetId(trimmedUuid);
|
||||
affinityGroupIds.add(affinityGroupId);
|
||||
}
|
||||
return affinityGroupIds;
|
||||
}
|
||||
|
||||
protected void addNodeTypeAffinityGroupEntry(String nodeType, List<Long> affinityGroupIds, Map<String, List<Long>> nodeTypeToAffinityGroupIds) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Node Type: '%s' should use affinity group IDs: '%s'", nodeType, affinityGroupIds));
|
||||
}
|
||||
KubernetesClusterNodeType clusterNodeType = KubernetesClusterNodeType.valueOf(nodeType.toUpperCase());
|
||||
nodeTypeToAffinityGroupIds.put(clusterNodeType.name(), affinityGroupIds);
|
||||
}
|
||||
|
||||
protected void processNodeTypeAffinityGroupEntryAndAddToMappingIfValid(Map<String, String> nodeTypeAffinityConfig, Map<String, List<Long>> nodeTypeToAffinityGroupIds) {
|
||||
if (MapUtils.isEmpty(nodeTypeAffinityConfig)) {
|
||||
return;
|
||||
}
|
||||
String nodeType = nodeTypeAffinityConfig.get(VmDetailConstants.CKS_NODE_TYPE);
|
||||
String affinityGroupUuids = nodeTypeAffinityConfig.get(VmDetailConstants.AFFINITY_GROUP);
|
||||
checkNodeTypeAffinityGroupEntryCompleteness(nodeType, affinityGroupUuids);
|
||||
checkNodeTypeAffinityGroupEntryNodeType(nodeType);
|
||||
|
||||
List<Long> affinityGroupIds = validateAndGetAffinityGroupIds(affinityGroupUuids);
|
||||
addNodeTypeAffinityGroupEntry(nodeType, affinityGroupIds, nodeTypeToAffinityGroupIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Long>> getAffinityGroupNodeTypeMap(Map<String, Map<String, String>> affinityGroupNodeTypeMap) {
|
||||
Map<String, List<Long>> nodeTypeToAffinityGroupIds = new HashMap<>();
|
||||
if (MapUtils.isNotEmpty(affinityGroupNodeTypeMap)) {
|
||||
for (Map<String, String> nodeTypeAffinityConfig : affinityGroupNodeTypeMap.values()) {
|
||||
processNodeTypeAffinityGroupEntryAndAddToMappingIfValid(nodeTypeAffinityConfig, nodeTypeToAffinityGroupIds);
|
||||
}
|
||||
}
|
||||
return nodeTypeToAffinityGroupIds;
|
||||
}
|
||||
|
||||
public void cleanupForAccount(Account account) {
|
||||
kubernetesClusterService.cleanupForAccount(account);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -63,7 +65,9 @@ import com.cloud.vm.VirtualMachine;
|
|||
import com.cloud.vm.dao.NicDao;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.AffinityProcessorBase;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd;
|
||||
|
|
@ -90,6 +94,7 @@ import com.cloud.kubernetes.cluster.KubernetesClusterDetailsVO;
|
|||
import com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl;
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterVO;
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterVmMapVO;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterAffinityGroupMapDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDetailsDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||
|
|
@ -124,10 +129,12 @@ import com.cloud.utils.fsm.NoTransitionException;
|
|||
import com.cloud.utils.fsm.StateMachine2;
|
||||
import com.cloud.utils.ssh.SshHelper;
|
||||
import com.cloud.vm.VMInstanceDetailVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.UserVmService;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.dao.VMInstanceDetailsDao;
|
||||
|
||||
import static com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.CONTROL;
|
||||
|
|
@ -213,10 +220,15 @@ public class KubernetesClusterActionWorker {
|
|||
private NicDao nicDao;
|
||||
@Inject
|
||||
protected AffinityGroupDao affinityGroupDao;
|
||||
@Inject
|
||||
protected AffinityGroupVMMapDao affinityGroupVMMapDao;
|
||||
@Inject
|
||||
protected VMInstanceDao vmInstanceDao;
|
||||
|
||||
protected KubernetesClusterDao kubernetesClusterDao;
|
||||
protected KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
|
||||
protected KubernetesClusterDetailsDao kubernetesClusterDetailsDao;
|
||||
protected KubernetesClusterAffinityGroupMapDao kubernetesClusterAffinityGroupMapDao;
|
||||
protected KubernetesSupportedVersionDao kubernetesSupportedVersionDao;
|
||||
|
||||
protected KubernetesCluster kubernetesCluster;
|
||||
|
|
@ -251,6 +263,7 @@ public class KubernetesClusterActionWorker {
|
|||
this.kubernetesClusterDao = clusterManager.kubernetesClusterDao;
|
||||
this.kubernetesClusterDetailsDao = clusterManager.kubernetesClusterDetailsDao;
|
||||
this.kubernetesClusterVmMapDao = clusterManager.kubernetesClusterVmMapDao;
|
||||
this.kubernetesClusterAffinityGroupMapDao = clusterManager.kubernetesClusterAffinityGroupMapDao;
|
||||
this.kubernetesSupportedVersionDao = clusterManager.kubernetesSupportedVersionDao;
|
||||
this.manager = clusterManager;
|
||||
}
|
||||
|
|
@ -1112,4 +1125,76 @@ public class KubernetesClusterActionWorker {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected List<Long> getAffinityGroupIdsForNodeType(KubernetesClusterNodeType nodeType) {
|
||||
List<Long> affinityGroupIds = kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(
|
||||
kubernetesCluster.getId(), nodeType.name());
|
||||
if (CollectionUtils.isEmpty(affinityGroupIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return new ArrayList<>(affinityGroupIds);
|
||||
}
|
||||
|
||||
protected List<Long> getMergedAffinityGroupIds(KubernetesClusterNodeType nodeType, Long domainId, Long accountId) {
|
||||
List<Long> affinityGroupIds = getAffinityGroupIdsForNodeType(nodeType);
|
||||
Long explicitAffinityGroupId = getExplicitAffinityGroup(domainId, accountId);
|
||||
if (explicitAffinityGroupId != null && !affinityGroupIds.contains(explicitAffinityGroupId)) {
|
||||
affinityGroupIds.add(explicitAffinityGroupId);
|
||||
}
|
||||
return affinityGroupIds.isEmpty() ? null : affinityGroupIds;
|
||||
}
|
||||
|
||||
private Set<Long> getRunningVmHostIds(Long affinityGroupId) {
|
||||
return affinityGroupVMMapDao.listVmIdsByAffinityGroup(affinityGroupId).stream()
|
||||
.map(vmInstanceDao::findById)
|
||||
.filter(vm -> Objects.nonNull(vm) && Objects.nonNull(vm.getHostId()) && VirtualMachine.State.Running.equals(vm.getState()))
|
||||
.map(VMInstanceVO::getHostId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
protected AffinityConstraints resolveAffinityConstraints(KubernetesClusterNodeType nodeType, Long domainId, Long accountId) {
|
||||
Set<Long> antiAffinityOccupiedHosts = new HashSet<>();
|
||||
Long requiredHostId = null;
|
||||
boolean hasHostAntiAffinity = false;
|
||||
boolean hasHostAffinity = false;
|
||||
|
||||
if (Objects.nonNull(nodeType)) {
|
||||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(nodeType, domainId, accountId);
|
||||
if (CollectionUtils.isNotEmpty(affinityGroupIds)) {
|
||||
for (Long affinityGroupId : affinityGroupIds) {
|
||||
AffinityGroupVO affinityGroup = affinityGroupDao.findById(affinityGroupId);
|
||||
if (Objects.isNull(affinityGroup)) {
|
||||
continue;
|
||||
}
|
||||
if (AffinityProcessorBase.AFFINITY_TYPE_HOST_ANTI.equals(affinityGroup.getType())) {
|
||||
hasHostAntiAffinity = true;
|
||||
antiAffinityOccupiedHosts.addAll(getRunningVmHostIds(affinityGroupId));
|
||||
} else if (AffinityProcessorBase.AFFINITY_TYPE_HOST.equals(affinityGroup.getType())) {
|
||||
hasHostAffinity = true;
|
||||
Set<Long> hostIds = getRunningVmHostIds(affinityGroupId);
|
||||
if (CollectionUtils.isNotEmpty(hostIds)) {
|
||||
requiredHostId = hostIds.iterator().next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new AffinityConstraints(hasHostAntiAffinity, hasHostAffinity, antiAffinityOccupiedHosts, requiredHostId);
|
||||
}
|
||||
|
||||
protected static class AffinityConstraints {
|
||||
final boolean hasHostAntiAffinity;
|
||||
final boolean hasHostAffinity;
|
||||
final Set<Long> antiAffinityOccupiedHosts;
|
||||
final Long requiredHostId;
|
||||
|
||||
AffinityConstraints(boolean hasHostAntiAffinity, boolean hasHostAffinity,
|
||||
Set<Long> antiAffinityOccupiedHosts, Long requiredHostId) {
|
||||
this.hasHostAntiAffinity = hasHostAntiAffinity;
|
||||
this.hasHostAffinity = hasHostAffinity;
|
||||
this.antiAffinityOccupiedHosts = antiAffinityOccupiedHosts;
|
||||
this.requiredHostId = requiredHostId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod
|
|||
stateTransitTo(kubernetesCluster.getId(), KubernetesCluster.Event.OperationSucceeded);
|
||||
annotationDao.removeByEntityType(AnnotationService.EntityType.KUBERNETES_CLUSTER.name(), kubernetesCluster.getUuid());
|
||||
kubernetesClusterDetailsDao.removeDetails(kubernetesCluster.getId());
|
||||
kubernetesClusterAffinityGroupMapDao.removeByClusterId(kubernetesCluster.getId());
|
||||
boolean deleted = kubernetesClusterDao.remove(kubernetesCluster.getId());
|
||||
if (!deleted) {
|
||||
logMessage(Level.WARN, String.format("Failed to delete Kubernetes cluster: %s", kubernetesCluster), null);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import static com.cloud.utils.db.Transaction.execute;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -116,7 +115,6 @@ import com.cloud.vm.Nic;
|
|||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
|
@ -152,8 +150,6 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
@Inject
|
||||
protected LoadBalancerDao loadBalancerDao;
|
||||
@Inject
|
||||
protected VMInstanceDao vmInstanceDao;
|
||||
@Inject
|
||||
protected UserVmManager userVmManager;
|
||||
@Inject
|
||||
protected LaunchPermissionDao launchPermissionDao;
|
||||
|
|
@ -177,8 +173,33 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
kubernetesClusterNodeNamePrefix = getKubernetesClusterNodeNamePrefix();
|
||||
}
|
||||
|
||||
protected List<HostVO> filterHostsByAffinityConstraints(List<HostVO> hosts, AffinityConstraints constraints, DataCenter zone)
|
||||
throws InsufficientServerCapacityException {
|
||||
if (constraints.hasHostAffinity && Objects.nonNull(constraints.requiredHostId)) {
|
||||
hosts = hosts.stream().filter(host -> host.getId() == constraints.requiredHostId.longValue()).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(hosts)) {
|
||||
String msg = String.format("Cannot find capacity for Kubernetes cluster: host affinity requires all VMs on host %d but it is not available in zone %s",
|
||||
constraints.requiredHostId, zone.getName());
|
||||
throw new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId());
|
||||
}
|
||||
}
|
||||
|
||||
if (constraints.hasHostAntiAffinity) {
|
||||
hosts = hosts.stream().filter(host -> !constraints.antiAffinityOccupiedHosts.contains(host.getId())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(hosts)) {
|
||||
String msg = String.format("Cannot find capacity for Kubernetes cluster: host anti-affinity requires each VM on a separate host, " +
|
||||
"but all %d available hosts in zone %s are already occupied by existing cluster VMs",
|
||||
constraints.antiAffinityOccupiedHosts.size(), zone.getName());
|
||||
throw new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return hosts;
|
||||
}
|
||||
|
||||
protected DeployDestination plan(final long nodesCount, final DataCenter zone, final ServiceOffering offering,
|
||||
final Long domainId, final Long accountId, final Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) throws InsufficientServerCapacityException {
|
||||
final Long domainId, final Long accountId, final Hypervisor.HypervisorType hypervisorType,
|
||||
CPU.CPUArch arch, KubernetesClusterNodeType nodeType) throws InsufficientServerCapacityException {
|
||||
final int cpu_requested = offering.getCpu() * offering.getSpeed();
|
||||
final long ram_requested = offering.getRamSize() * 1024L * 1024L;
|
||||
boolean useDedicatedHosts = false;
|
||||
|
|
@ -191,26 +212,30 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
} else if (Objects.nonNull(domainId)) {
|
||||
dedicatedHosts = dedicatedResourceDao.listByDomainId(domainId);
|
||||
}
|
||||
for (DedicatedResourceVO dedicatedHost : dedicatedHosts) {
|
||||
hosts.add(hostDao.findById(dedicatedHost.getHostId()));
|
||||
for (DedicatedResourceVO dedicatedResource : dedicatedHosts) {
|
||||
hosts.addAll(manager.getHostsForDedicatedResource(dedicatedResource, zone));
|
||||
useDedicatedHosts = true;
|
||||
}
|
||||
}
|
||||
if (hosts.isEmpty()) {
|
||||
hosts = resourceManager.listAllHostsInOneZoneByType(Host.Type.Routing, zone.getId());
|
||||
}
|
||||
if (hypervisorType != null) {
|
||||
if (Objects.nonNull(hypervisorType)) {
|
||||
hosts = hosts.stream().filter(x -> x.getHypervisorType() == hypervisorType).collect(Collectors.toList());
|
||||
}
|
||||
if (arch != null) {
|
||||
if (Objects.nonNull(arch)) {
|
||||
hosts = hosts.stream().filter(x -> x.getArch().equals(arch)).collect(Collectors.toList());
|
||||
}
|
||||
if (CollectionUtils.isEmpty(hosts)) {
|
||||
String msg = String.format("Cannot find enough capacity for Kubernetes cluster(requested cpu=%d memory=%s) with offering: %s hypervisor: %s and arch: %s",
|
||||
cpu_requested * nodesCount, toHumanReadableSize(ram_requested * nodesCount), offering.getName(), clusterTemplate.getHypervisorType().toString(), arch.getType());
|
||||
cpu_requested * nodesCount, toHumanReadableSize(ram_requested * nodesCount), offering.getName(), clusterTemplate.getHypervisorType().toString(),
|
||||
Objects.nonNull(arch) ? arch.getType() : "null");
|
||||
logAndThrow(Level.WARN, msg, new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId()));
|
||||
}
|
||||
|
||||
AffinityConstraints affinityConstraints = resolveAffinityConstraints(nodeType, domainId, accountId);
|
||||
hosts = filterHostsByAffinityConstraints(hosts, affinityConstraints, zone);
|
||||
|
||||
final Map<String, Pair<HostVO, Integer>> hosts_with_resevered_capacity = new ConcurrentHashMap<String, Pair<HostVO, Integer>>();
|
||||
for (HostVO h : hosts) {
|
||||
hosts_with_resevered_capacity.put(h.getUuid(), new Pair<HostVO, Integer>(h, 0));
|
||||
|
|
@ -230,6 +255,9 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
continue;
|
||||
}
|
||||
int reserved = hp.second();
|
||||
if (affinityConstraints.hasHostAntiAffinity && reserved > 0) {
|
||||
continue;
|
||||
}
|
||||
reserved++;
|
||||
ClusterVO cluster = clusterDao.findById(h.getClusterId());
|
||||
ClusterDetailsVO cluster_detail_cpu = clusterDetailsDao.findDetail(cluster.getId(), "cpuOvercommitRatio");
|
||||
|
|
@ -264,10 +292,17 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
return new DeployDestination(zone, null, null, null);
|
||||
}
|
||||
String msg = String.format("Cannot find enough capacity for Kubernetes cluster(requested cpu=%d memory=%s) with offering: %s hypervisor: %s and arch: %s",
|
||||
cpu_requested * nodesCount, toHumanReadableSize(ram_requested * nodesCount), offering.getName(), clusterTemplate.getHypervisorType().toString(), arch.getType());
|
||||
|
||||
logger.warn(msg);
|
||||
String msg;
|
||||
if (affinityConstraints.hasHostAntiAffinity) {
|
||||
msg = String.format("Cannot find enough capacity for Kubernetes cluster (requested cpu=%d memory=%s) with offering: %s. " +
|
||||
"Host anti-affinity requires %d separate hosts but not enough suitable hosts are available in zone %s",
|
||||
cpu_requested * nodesCount, toHumanReadableSize(ram_requested * nodesCount), offering.getName(),
|
||||
nodesCount, zone.getName());
|
||||
} else {
|
||||
msg = String.format("Cannot find enough capacity for Kubernetes cluster(requested cpu=%d memory=%s) with offering: %s hypervisor: %s and arch: %s",
|
||||
cpu_requested * nodesCount, toHumanReadableSize(ram_requested * nodesCount), offering.getName(), clusterTemplate.getHypervisorType().toString(),
|
||||
Objects.nonNull(arch) ? arch.getType() : "null");
|
||||
}
|
||||
throw new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId());
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +331,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Checking deployment destination for {} nodes on Kubernetes cluster : {} in zone : {}", nodeType.name(), kubernetesCluster.getName(), zone.getName());
|
||||
}
|
||||
DeployDestination planForNodeType = plan(nodes, zone, nodeOffering, domainId, accountId, hypervisorType, arch);
|
||||
DeployDestination planForNodeType = plan(nodes, zone, nodeOffering, domainId, accountId, hypervisorType, arch, nodeType);
|
||||
destinationMap.put(nodeType.name(), planForNodeType);
|
||||
}
|
||||
return destinationMap;
|
||||
|
|
@ -426,21 +461,19 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
if (StringUtils.isNotBlank(kubernetesCluster.getKeyPair())) {
|
||||
keypairs.add(kubernetesCluster.getKeyPair());
|
||||
}
|
||||
Long affinityGroupId = getExplicitAffinityGroup(domainId, accountId);
|
||||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(WORKER, domainId, accountId);
|
||||
if (kubernetesCluster.getSecurityGroupId() != null && networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds, List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
List<Long> securityGroupIds = new ArrayList<>();
|
||||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
nodeVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, workerNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null,
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE, null, null);
|
||||
} else {
|
||||
nodeVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, workerNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created node VM : {}, {} in the Kubernetes cluster : {}", hostName, nodeVm, kubernetesCluster.getName());
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
|
|
@ -63,7 +63,6 @@ import com.cloud.utils.ssh.SshHelper;
|
|||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import static com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.CONTROL;
|
||||
|
|
@ -73,9 +72,6 @@ import static com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClu
|
|||
|
||||
public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModifierActionWorker {
|
||||
|
||||
@Inject
|
||||
protected VMInstanceDao vmInstanceDao;
|
||||
|
||||
private Map<String, ServiceOffering> serviceOfferingNodeTypeMap;
|
||||
private Long clusterSize;
|
||||
private List<Long> nodeIds;
|
||||
|
|
@ -325,7 +321,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
}
|
||||
|
||||
private void validateKubernetesClusterScaleSizeParameters() throws CloudRuntimeException {
|
||||
private void validateKubernetesClusterScaleSizeParameters(KubernetesClusterNodeType nodeType) throws CloudRuntimeException {
|
||||
final long originalClusterSize = kubernetesCluster.getNodeCount();
|
||||
if (network == null) {
|
||||
logTransitStateToFailedIfNeededAndThrow(Level.WARN, String.format("Scaling failed for Kubernetes cluster : %s, cluster network not found", kubernetesCluster.getName()));
|
||||
|
|
@ -341,12 +337,12 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
VMTemplateVO clusterTemplate = templateDao.findById(kubernetesCluster.getTemplateId());
|
||||
try {
|
||||
if (originalState.equals(KubernetesCluster.State.Running)) {
|
||||
plan(newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId(), clusterTemplate.getHypervisorType(), clusterTemplate.getArch());
|
||||
plan(newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId(), clusterTemplate.getHypervisorType(), clusterTemplate.getArch(), nodeType);
|
||||
} else {
|
||||
plan(kubernetesCluster.getTotalNodeCount() + newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId(), clusterTemplate.getHypervisorType(), clusterTemplate.getArch());
|
||||
plan(kubernetesCluster.getTotalNodeCount() + newVmRequiredCount, zone, clusterServiceOffering, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId(), clusterTemplate.getHypervisorType(), clusterTemplate.getArch(), nodeType);
|
||||
}
|
||||
} catch (InsufficientCapacityException e) {
|
||||
logTransitStateToFailedIfNeededAndThrow(Level.WARN, String.format("Scaling failed for Kubernetes cluster : %s in zone : %s, insufficient capacity", kubernetesCluster.getName(), zone.getName()));
|
||||
logTransitStateToFailedIfNeededAndThrow(Level.WARN, String.format("Scaling failed for Kubernetes cluster : %s in zone : %s, insufficient capacity: %s", kubernetesCluster.getName(), zone.getName(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
List<KubernetesClusterVmMapVO> vmList = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
|
||||
|
|
@ -465,10 +461,38 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
return new ArrayList<>(workerVMsMap.subList(startIndex, totalWorkerNodes));
|
||||
}
|
||||
|
||||
private void cleanupNewlyCreatedVms(Set<Long> originalVmIds) {
|
||||
List<KubernetesClusterVmMapVO> currentVmMaps = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
|
||||
for (KubernetesClusterVmMapVO clusterVmMap : currentVmMaps) {
|
||||
if (originalVmIds.contains(clusterVmMap.getVmId())) {
|
||||
continue;
|
||||
}
|
||||
UserVmVO userVM = userVmDao.findById(clusterVmMap.getVmId());
|
||||
if (Objects.isNull(userVM)) {
|
||||
kubernetesClusterVmMapDao.expunge(clusterVmMap.getId());
|
||||
continue;
|
||||
}
|
||||
logger.warn("Cleaning up VM {} created during failed scale-up of Kubernetes cluster {}", userVM, kubernetesCluster);
|
||||
CallContext vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||
vmContext.setEventResourceId(userVM.getId());
|
||||
try {
|
||||
userVmService.destroyVm(userVM.getId(), true);
|
||||
userVmManager.expunge(userVM);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to cleanup VM {} during scale-up rollback for Kubernetes cluster {}", userVM, kubernetesCluster, e);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
kubernetesClusterVmMapDao.expunge(clusterVmMap.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void scaleUpKubernetesClusterSize(final long newVmCount) throws CloudRuntimeException {
|
||||
if (!kubernetesCluster.getState().equals(KubernetesCluster.State.Scaling)) {
|
||||
stateTransitTo(kubernetesCluster.getId(), KubernetesCluster.Event.ScaleUpRequested);
|
||||
}
|
||||
Set<Long> originalVmIds = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId())
|
||||
.stream().map(KubernetesClusterVmMapVO::getVmId).collect(Collectors.toSet());
|
||||
List<UserVm> clusterVMs = new ArrayList<>();
|
||||
if (isDefaultTemplateUsed()) {
|
||||
LaunchPermissionVO launchPermission = new LaunchPermissionVO(clusterTemplate.getId(), owner.getId());
|
||||
|
|
@ -478,6 +502,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
clusterVMs = provisionKubernetesClusterNodeVms((int)(newVmCount + kubernetesCluster.getNodeCount()), (int)kubernetesCluster.getNodeCount(), publicIpAddress, kubernetesCluster.getDomainId(), kubernetesCluster.getAccountId());
|
||||
updateLoginUserDetails(clusterVMs.stream().map(InternalIdentity::getId).collect(Collectors.toList()));
|
||||
} catch (CloudRuntimeException | ManagementServerException | ResourceUnavailableException | InsufficientCapacityException e) {
|
||||
cleanupNewlyCreatedVms(originalVmIds);
|
||||
logTransitStateToFailedIfNeededAndThrow(Level.ERROR, String.format("Scaling failed for Kubernetes cluster : %s, unable to provision node VM in the cluster", kubernetesCluster.getName()), e);
|
||||
}
|
||||
try {
|
||||
|
|
@ -486,6 +511,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
clusterVMIds.addAll(externalNodeIds);
|
||||
scaleKubernetesClusterNetworkRules(clusterVMIds);
|
||||
} catch (ManagementServerException e) {
|
||||
cleanupNewlyCreatedVms(originalVmIds);
|
||||
logTransitStateToFailedIfNeededAndThrow(Level.ERROR, String.format("Scaling failed for Kubernetes cluster : %s, unable to update network rules", kubernetesCluster.getName()), e);
|
||||
}
|
||||
attachIsoKubernetesVMs(clusterVMs);
|
||||
|
|
@ -496,12 +522,13 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
|||
detachIsoKubernetesVMs(clusterVMs);
|
||||
deleteTemplateLaunchPermission();
|
||||
if (!readyNodesCountValid) { // Scaling failed
|
||||
cleanupNewlyCreatedVms(originalVmIds);
|
||||
logTransitStateToFailedIfNeededAndThrow(Level.ERROR, String.format("Scaling unsuccessful for Kubernetes cluster : %s as it does not have desired number of nodes in ready state", kubernetesCluster.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void scaleKubernetesClusterSize(KubernetesClusterNodeType nodeType) throws CloudRuntimeException {
|
||||
validateKubernetesClusterScaleSizeParameters();
|
||||
validateKubernetesClusterScaleSizeParameters(nodeType);
|
||||
final long originalClusterSize = kubernetesCluster.getNodeCount();
|
||||
final long newVmRequiredCount = clusterSize - originalClusterSize;
|
||||
if (KubernetesCluster.State.Created.equals(originalState)) {
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
keypairs.add(kubernetesCluster.getKeyPair());
|
||||
}
|
||||
|
||||
Long affinityGroupId = getExplicitAffinityGroup(domainId, accountId);
|
||||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(CONTROL, domainId, accountId);
|
||||
String userDataDetails = kubernetesCluster.getCniConfigDetails();
|
||||
if (kubernetesCluster.getSecurityGroupId() != null &&
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
|
|
@ -279,15 +279,13 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
controlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, userDataId, userDataDetails, keypairs,
|
||||
requestedIps, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null,
|
||||
requestedIps, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE, null, null);
|
||||
} else {
|
||||
controlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, userDataId, userDataDetails, keypairs,
|
||||
requestedIps, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
requestedIps, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created control VM: {}, {} in the Kubernetes cluster: {}", controlVm, hostName, kubernetesCluster);
|
||||
|
|
@ -439,7 +437,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
keypairs.add(kubernetesCluster.getKeyPair());
|
||||
}
|
||||
|
||||
Long affinityGroupId = getExplicitAffinityGroup(domainId, accountId);
|
||||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(CONTROL, domainId, accountId);
|
||||
if (kubernetesCluster.getSecurityGroupId() != null &&
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
|
|
@ -447,15 +445,13 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
additionalControlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null,
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE, null, null);
|
||||
} else {
|
||||
additionalControlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -483,7 +479,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
if (StringUtils.isNotBlank(kubernetesCluster.getKeyPair())) {
|
||||
keypairs.add(kubernetesCluster.getKeyPair());
|
||||
}
|
||||
Long affinityGroupId = getExplicitAffinityGroup(domainId, accountId);
|
||||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(ETCD, domainId, accountId);
|
||||
String hostName = etcdNodeHostnames.get(etcdNodeIndex);
|
||||
Map<String, String> customParameterMap = new HashMap<String, String>();
|
||||
if (zone.isSecurityGroupEnabled()) {
|
||||
|
|
@ -491,15 +487,13 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
etcdNode = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, etcdTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null,
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, null, null, null);
|
||||
} else {
|
||||
etcdNode = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, etcdTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// 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 com.cloud.kubernetes.cluster.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterAffinityGroupMapVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface KubernetesClusterAffinityGroupMapDao extends GenericDao<KubernetesClusterAffinityGroupMapVO, Long> {
|
||||
|
||||
List<KubernetesClusterAffinityGroupMapVO> listByClusterIdAndNodeType(long clusterId, String nodeType);
|
||||
|
||||
List<Long> listAffinityGroupIdsByClusterIdAndNodeType(long clusterId, String nodeType);
|
||||
|
||||
int removeByClusterId(long clusterId);
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// 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 com.cloud.kubernetes.cluster.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterAffinityGroupMapVO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
@Component
|
||||
public class KubernetesClusterAffinityGroupMapDaoImpl extends GenericDaoBase<KubernetesClusterAffinityGroupMapVO, Long>
|
||||
implements KubernetesClusterAffinityGroupMapDao {
|
||||
|
||||
private final SearchBuilder<KubernetesClusterAffinityGroupMapVO> clusterIdAndNodeTypeSearch;
|
||||
private final SearchBuilder<KubernetesClusterAffinityGroupMapVO> clusterIdSearch;
|
||||
|
||||
public KubernetesClusterAffinityGroupMapDaoImpl() {
|
||||
clusterIdAndNodeTypeSearch = createSearchBuilder();
|
||||
clusterIdAndNodeTypeSearch.and("clusterId", clusterIdAndNodeTypeSearch.entity().getClusterId(), SearchCriteria.Op.EQ);
|
||||
clusterIdAndNodeTypeSearch.and("nodeType", clusterIdAndNodeTypeSearch.entity().getNodeType(), SearchCriteria.Op.EQ);
|
||||
clusterIdAndNodeTypeSearch.done();
|
||||
|
||||
clusterIdSearch = createSearchBuilder();
|
||||
clusterIdSearch.and("clusterId", clusterIdSearch.entity().getClusterId(), SearchCriteria.Op.EQ);
|
||||
clusterIdSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KubernetesClusterAffinityGroupMapVO> listByClusterIdAndNodeType(long clusterId, String nodeType) {
|
||||
SearchCriteria<KubernetesClusterAffinityGroupMapVO> sc = clusterIdAndNodeTypeSearch.create();
|
||||
sc.setParameters("clusterId", clusterId);
|
||||
sc.setParameters("nodeType", nodeType);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listAffinityGroupIdsByClusterIdAndNodeType(long clusterId, String nodeType) {
|
||||
List<KubernetesClusterAffinityGroupMapVO> maps = listByClusterIdAndNodeType(clusterId, nodeType);
|
||||
if (CollectionUtils.isEmpty(maps)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return maps.stream().map(KubernetesClusterAffinityGroupMapVO::getAffinityGroupId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeByClusterId(long clusterId) {
|
||||
SearchCriteria<KubernetesClusterAffinityGroupMapVO> sc = clusterIdSearch.create();
|
||||
sc.setParameters("clusterId", clusterId);
|
||||
return remove(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package org.apache.cloudstack.api.command.user.kubernetes.cluster;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
|
|||
@Inject
|
||||
public KubernetesClusterService kubernetesClusterService;
|
||||
@Inject
|
||||
protected KubernetesServiceHelper kubernetesClusterHelper;
|
||||
protected KubernetesServiceHelper kubernetesServiceHelper;
|
||||
@Inject
|
||||
private ConfigurationDao configurationDao;
|
||||
@Inject
|
||||
|
|
@ -125,6 +126,12 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
|
|||
since = "4.21.0")
|
||||
private Map<String, Map<String, String>> templateNodeTypeMap;
|
||||
|
||||
@ACL(accessType = AccessType.UseEntry)
|
||||
@Parameter(name = ApiConstants.NODE_TYPE_AFFINITY_GROUP_MAP, type = CommandType.MAP,
|
||||
description = "(Optional) Node Type to Affinity Group ID mapping. If provided, VMs of each node type will be added to the specified affinity group",
|
||||
since = "4.23.0")
|
||||
private Map<String, Map<String, String>> affinityGroupNodeTypeMap;
|
||||
|
||||
@ACL(accessType = AccessType.UseEntry)
|
||||
@Parameter(name = ApiConstants.ETCD_NODES, type = CommandType.LONG,
|
||||
description = "(Optional) Number of Kubernetes cluster etcd nodes, default is 0." +
|
||||
|
|
@ -314,11 +321,15 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
|
|||
}
|
||||
|
||||
public Map<String, Long> getServiceOfferingNodeTypeMap() {
|
||||
return kubernetesClusterHelper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap);
|
||||
return kubernetesServiceHelper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap);
|
||||
}
|
||||
|
||||
public Map<String, Long> getTemplateNodeTypeMap() {
|
||||
return kubernetesClusterHelper.getTemplateNodeTypeMap(templateNodeTypeMap);
|
||||
return kubernetesServiceHelper.getTemplateNodeTypeMap(templateNodeTypeMap);
|
||||
}
|
||||
|
||||
public Map<String, List<Long>> getAffinityGroupNodeTypeMap() {
|
||||
return kubernetesServiceHelper.getAffinityGroupNodeTypeMap(affinityGroupNodeTypeMap);
|
||||
}
|
||||
|
||||
public Hypervisor.HypervisorType getHypervisorType() {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class ScaleKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
@Inject
|
||||
public KubernetesClusterService kubernetesClusterService;
|
||||
@Inject
|
||||
protected KubernetesServiceHelper kubernetesClusterHelper;
|
||||
protected KubernetesServiceHelper kubernetesServiceHelper;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
|
|
@ -114,7 +114,7 @@ public class ScaleKubernetesClusterCmd extends BaseAsyncCmd {
|
|||
}
|
||||
|
||||
public Map<String, Long> getServiceOfferingNodeTypeMap() {
|
||||
return kubernetesClusterHelper.getServiceOfferingNodeTypeMap(this.serviceOfferingNodeTypeMap);
|
||||
return kubernetesServiceHelper.getServiceOfferingNodeTypeMap(this.serviceOfferingNodeTypeMap);
|
||||
}
|
||||
|
||||
public Long getClusterSize() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
// 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.apache.cloudstack.api.command.user.kubernetes.cluster;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.KubernetesClusterResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.kubernetes.cluster.KubernetesCluster;
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterService;
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
@APICommand(name = "updateKubernetesClusterAffinityGroups",
|
||||
description = "Updates the affinity group mappings for a stopped Kubernetes cluster",
|
||||
responseObject = KubernetesClusterResponse.class,
|
||||
responseView = ResponseObject.ResponseView.Restricted,
|
||||
entityType = {KubernetesCluster.class},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = true,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class UpdateKubernetesClusterAffinityGroupCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
public KubernetesClusterService kubernetesClusterService;
|
||||
@Inject
|
||||
protected KubernetesServiceHelper kubernetesServiceHelper;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true,
|
||||
entityType = KubernetesClusterResponse.class,
|
||||
description = "The ID of the Kubernetes cluster")
|
||||
private Long id;
|
||||
|
||||
@ACL(accessType = SecurityChecker.AccessType.UseEntry)
|
||||
@Parameter(name = ApiConstants.NODE_TYPE_AFFINITY_GROUP_MAP, type = CommandType.MAP,
|
||||
description = "Node Type to Affinity Group ID mapping. VMs of each node type will be added to the specified affinity group",
|
||||
since = "4.23.0")
|
||||
private Map<String, Map<String, String>> affinityGroupNodeTypeMap;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Map<String, List<Long>> getAffinityGroupNodeTypeMap() {
|
||||
return kubernetesServiceHelper.getAffinityGroupNodeTypeMap(affinityGroupNodeTypeMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KubernetesCluster;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() throws ServerApiException {
|
||||
try {
|
||||
if (!kubernetesClusterService.updateKubernetesClusterAffinityGroups(this)) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
|
||||
String.format("Failed to update affinity groups for Kubernetes cluster ID: %d", getId()));
|
||||
}
|
||||
final KubernetesClusterResponse response = kubernetesClusterService.createKubernetesClusterResponse(getId());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (CloudRuntimeException exception) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -220,6 +220,30 @@ public class KubernetesClusterResponse extends BaseResponseWithAnnotations imple
|
|||
@Param(description = "The date when this Kubernetes cluster was created")
|
||||
private Date created;
|
||||
|
||||
@SerializedName(ApiConstants.CONTROL_AFFINITY_GROUP_IDS)
|
||||
@Param(description = "The IDs of affinity groups associated with control nodes", since = "4.23.0")
|
||||
private String controlAffinityGroupIds;
|
||||
|
||||
@SerializedName(ApiConstants.CONTROL_AFFINITY_GROUP_NAMES)
|
||||
@Param(description = "The names of affinity groups associated with control nodes", since = "4.23.0")
|
||||
private String controlAffinityGroupNames;
|
||||
|
||||
@SerializedName(ApiConstants.WORKER_AFFINITY_GROUP_IDS)
|
||||
@Param(description = "The IDs of affinity groups associated with worker nodes", since = "4.23.0")
|
||||
private String workerAffinityGroupIds;
|
||||
|
||||
@SerializedName(ApiConstants.WORKER_AFFINITY_GROUP_NAMES)
|
||||
@Param(description = "The names of affinity groups associated with worker nodes", since = "4.23.0")
|
||||
private String workerAffinityGroupNames;
|
||||
|
||||
@SerializedName(ApiConstants.ETCD_AFFINITY_GROUP_IDS)
|
||||
@Param(description = "The IDs of affinity groups associated with etcd nodes", since = "4.23.0")
|
||||
private String etcdAffinityGroupIds;
|
||||
|
||||
@SerializedName(ApiConstants.ETCD_AFFINITY_GROUP_NAMES)
|
||||
@Param(description = "The names of affinity groups associated with etcd nodes", since = "4.23.0")
|
||||
private String etcdAffinityGroupNames;
|
||||
|
||||
public KubernetesClusterResponse() {
|
||||
}
|
||||
|
||||
|
|
@ -535,4 +559,28 @@ public class KubernetesClusterResponse extends BaseResponseWithAnnotations imple
|
|||
public void setCsiEnabled(Boolean csiEnabled) {
|
||||
isCsiEnabled = csiEnabled;
|
||||
}
|
||||
|
||||
public void setControlAffinityGroupIds(String controlAffinityGroupIds) {
|
||||
this.controlAffinityGroupIds = controlAffinityGroupIds;
|
||||
}
|
||||
|
||||
public void setControlAffinityGroupNames(String controlAffinityGroupNames) {
|
||||
this.controlAffinityGroupNames = controlAffinityGroupNames;
|
||||
}
|
||||
|
||||
public void setWorkerAffinityGroupIds(String workerAffinityGroupIds) {
|
||||
this.workerAffinityGroupIds = workerAffinityGroupIds;
|
||||
}
|
||||
|
||||
public void setWorkerAffinityGroupNames(String workerAffinityGroupNames) {
|
||||
this.workerAffinityGroupNames = workerAffinityGroupNames;
|
||||
}
|
||||
|
||||
public void setEtcdAffinityGroupIds(String etcdAffinityGroupIds) {
|
||||
this.etcdAffinityGroupIds = etcdAffinityGroupIds;
|
||||
}
|
||||
|
||||
public void setEtcdAffinityGroupNames(String etcdAffinityGroupNames) {
|
||||
this.etcdAffinityGroupNames = etcdAffinityGroupNames;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
<bean id="kubernetesClusterDaoImpl" class="com.cloud.kubernetes.cluster.dao.KubernetesClusterDaoImpl" />
|
||||
<bean id="kubernetesClusterDetailsDaoImpl" class="com.cloud.kubernetes.cluster.dao.KubernetesClusterDetailsDaoImpl" />
|
||||
<bean id="kubernetesClusterVmMapDaoImpl" class="com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDaoImpl" />
|
||||
<bean id="kubernetesClusterAffinityGroupMapDaoImpl" class="com.cloud.kubernetes.cluster.dao.KubernetesClusterAffinityGroupMapDaoImpl" />
|
||||
<bean id="kubernetesClusterManagerImpl" class="com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl" />
|
||||
|
||||
<bean id="kubernetesServiceHelper" class="com.cloud.kubernetes.cluster.KubernetesServiceHelperImpl" >
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
// 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 com.cloud.kubernetes.cluster;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class KubernetesClusterAffinityGroupMapVOTest {
|
||||
|
||||
@Test
|
||||
public void testConstructorAndGetters() {
|
||||
KubernetesClusterAffinityGroupMapVO vo =
|
||||
new KubernetesClusterAffinityGroupMapVO(1L, "CONTROL", 100L);
|
||||
|
||||
Assert.assertEquals(1L, vo.getClusterId());
|
||||
Assert.assertEquals("CONTROL", vo.getNodeType());
|
||||
Assert.assertEquals(100L, vo.getAffinityGroupId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultConstructor() {
|
||||
KubernetesClusterAffinityGroupMapVO vo = new KubernetesClusterAffinityGroupMapVO();
|
||||
Assert.assertNotNull(vo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetClusterId() {
|
||||
KubernetesClusterAffinityGroupMapVO vo = new KubernetesClusterAffinityGroupMapVO();
|
||||
vo.setClusterId(2L);
|
||||
Assert.assertEquals(2L, vo.getClusterId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNodeType() {
|
||||
KubernetesClusterAffinityGroupMapVO vo = new KubernetesClusterAffinityGroupMapVO();
|
||||
vo.setNodeType("WORKER");
|
||||
Assert.assertEquals("WORKER", vo.getNodeType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupId() {
|
||||
KubernetesClusterAffinityGroupMapVO vo = new KubernetesClusterAffinityGroupMapVO();
|
||||
vo.setAffinityGroupId(200L);
|
||||
Assert.assertEquals(200L, vo.getAffinityGroupId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllNodeTypes() {
|
||||
KubernetesClusterAffinityGroupMapVO controlVo =
|
||||
new KubernetesClusterAffinityGroupMapVO(1L, "CONTROL", 10L);
|
||||
KubernetesClusterAffinityGroupMapVO workerVo =
|
||||
new KubernetesClusterAffinityGroupMapVO(1L, "WORKER", 20L);
|
||||
KubernetesClusterAffinityGroupMapVO etcdVo =
|
||||
new KubernetesClusterAffinityGroupMapVO(1L, "ETCD", 30L);
|
||||
|
||||
Assert.assertEquals("CONTROL", controlVo.getNodeType());
|
||||
Assert.assertEquals("WORKER", workerVo.getNodeType());
|
||||
Assert.assertEquals("ETCD", etcdVo.getNodeType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettersChain() {
|
||||
KubernetesClusterAffinityGroupMapVO vo = new KubernetesClusterAffinityGroupMapVO();
|
||||
|
||||
vo.setClusterId(5L);
|
||||
vo.setNodeType("ETCD");
|
||||
vo.setAffinityGroupId(500L);
|
||||
|
||||
Assert.assertEquals(5L, vo.getClusterId());
|
||||
Assert.assertEquals("ETCD", vo.getNodeType());
|
||||
Assert.assertEquals(500L, vo.getAffinityGroupId());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
// 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 com.cloud.kubernetes.cluster;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.CONTROL;
|
||||
import static com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.ETCD;
|
||||
import static com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType.WORKER;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class KubernetesClusterHelperImplTest {
|
||||
|
||||
@Mock
|
||||
private ServiceOfferingDao serviceOfferingDao;
|
||||
@Mock
|
||||
private ServiceOfferingVO workerServiceOffering;
|
||||
@Mock
|
||||
private ServiceOfferingVO controlServiceOffering;
|
||||
@Mock
|
||||
private ServiceOfferingVO etcdServiceOffering;
|
||||
|
||||
private static final String workerNodesOfferingId = UUID.randomUUID().toString();
|
||||
private static final String controlNodesOfferingId = UUID.randomUUID().toString();
|
||||
private static final String etcdNodesOfferingId = UUID.randomUUID().toString();
|
||||
private static final Long workerOfferingId = 1L;
|
||||
private static final Long controlOfferingId = 2L;
|
||||
private static final Long etcdOfferingId = 3L;
|
||||
|
||||
private final KubernetesServiceHelperImpl helper = new KubernetesServiceHelperImpl();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
helper.serviceOfferingDao = serviceOfferingDao;
|
||||
Mockito.when(serviceOfferingDao.findByUuid(workerNodesOfferingId)).thenReturn(workerServiceOffering);
|
||||
Mockito.when(serviceOfferingDao.findByUuid(controlNodesOfferingId)).thenReturn(controlServiceOffering);
|
||||
Mockito.when(serviceOfferingDao.findByUuid(etcdNodesOfferingId)).thenReturn(etcdServiceOffering);
|
||||
Mockito.when(workerServiceOffering.getId()).thenReturn(workerOfferingId);
|
||||
Mockito.when(controlServiceOffering.getId()).thenReturn(controlOfferingId);
|
||||
Mockito.when(etcdServiceOffering.getId()).thenReturn(etcdOfferingId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidNodeTypeEmptyNodeType() {
|
||||
Assert.assertFalse(helper.isValidNodeType(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidNodeTypeInvalidNodeType() {
|
||||
String nodeType = "invalidNodeType";
|
||||
Assert.assertFalse(helper.isValidNodeType(nodeType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidNodeTypeValidNodeTypeLowercase() {
|
||||
String nodeType = KubernetesServiceHelper.KubernetesClusterNodeType.WORKER.name().toLowerCase();
|
||||
Assert.assertTrue(helper.isValidNodeType(nodeType));
|
||||
}
|
||||
|
||||
private Map<String, String> createMapEntry(KubernetesServiceHelper.KubernetesClusterNodeType nodeType,
|
||||
String nodeTypeOfferingUuid) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(VmDetailConstants.CKS_NODE_TYPE, nodeType.name().toLowerCase());
|
||||
map.put(VmDetailConstants.OFFERING, nodeTypeOfferingUuid);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeOfferingMap() {
|
||||
Map<String, Map<String, String>> serviceOfferingNodeTypeMap = new HashMap<>();
|
||||
Map<String, String> firstMap = createMapEntry(WORKER, workerNodesOfferingId);
|
||||
Map<String, String> secondMap = createMapEntry(CONTROL, controlNodesOfferingId);
|
||||
serviceOfferingNodeTypeMap.put("map1", firstMap);
|
||||
serviceOfferingNodeTypeMap.put("map2", secondMap);
|
||||
Map<String, Long> map = helper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap);
|
||||
Assert.assertNotNull(map);
|
||||
Assert.assertEquals(2, map.size());
|
||||
Assert.assertTrue(map.containsKey(WORKER.name()) && map.containsKey(CONTROL.name()));
|
||||
Assert.assertEquals(workerOfferingId, map.get(WORKER.name()));
|
||||
Assert.assertEquals(controlOfferingId, map.get(CONTROL.name()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeOfferingMapNullMap() {
|
||||
Map<String, Long> map = helper.getServiceOfferingNodeTypeMap(null);
|
||||
Assert.assertTrue(map.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeOfferingMapEtcdNodes() {
|
||||
Map<String, Map<String, String>> serviceOfferingNodeTypeMap = new HashMap<>();
|
||||
Map<String, String> firstMap = createMapEntry(ETCD, etcdNodesOfferingId);
|
||||
serviceOfferingNodeTypeMap.put("map1", firstMap);
|
||||
Map<String, Long> map = helper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap);
|
||||
Assert.assertNotNull(map);
|
||||
Assert.assertEquals(1, map.size());
|
||||
Assert.assertTrue(map.containsKey(ETCD.name()));
|
||||
Assert.assertEquals(etcdOfferingId, map.get(ETCD.name()));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeOfferingEntryCompletenessInvalidParameters() {
|
||||
helper.checkNodeTypeOfferingEntryCompleteness(WORKER.name(), null);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeOfferingEntryValuesInvalidNodeType() {
|
||||
String invalidNodeType = "invalidNodeTypeName";
|
||||
helper.checkNodeTypeOfferingEntryValues(invalidNodeType, workerServiceOffering, workerNodesOfferingId);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeOfferingEntryValuesEmptyOffering() {
|
||||
String nodeType = WORKER.name();
|
||||
helper.checkNodeTypeOfferingEntryValues(nodeType, null, workerNodesOfferingId);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import com.cloud.dc.DataCenter;
|
|||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterActionWorker;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterAffinityGroupMapDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
|
||||
|
|
@ -46,9 +47,14 @@ import com.cloud.utils.Pair;
|
|||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.AddVirtualMachinesToKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.command.user.kubernetes.cluster.RemoveVirtualMachinesFromKubernetesClusterCmd;
|
||||
import org.apache.cloudstack.api.response.KubernetesClusterResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
|
@ -103,6 +109,15 @@ public class KubernetesClusterManagerImplTest {
|
|||
@Mock
|
||||
private ServiceOfferingDao serviceOfferingDao;
|
||||
|
||||
@Mock
|
||||
private KubernetesClusterAffinityGroupMapDao kubernetesClusterAffinityGroupMapDao;
|
||||
|
||||
@Mock
|
||||
private AffinityGroupDao affinityGroupDao;
|
||||
|
||||
@Mock
|
||||
private HostDao hostDao;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
KubernetesClusterManagerImpl kubernetesClusterManager;
|
||||
|
|
@ -441,4 +456,462 @@ public class KubernetesClusterManagerImplTest {
|
|||
String cksClusterPreferredArch = kubernetesClusterManager.getCksClusterPreferredArch(systemVMArch, cksIso);
|
||||
Assert.assertEquals(CPU.CPUArch.amd64.getType(), cksClusterPreferredArch);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupResponseForNodeTypeControl() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
AffinityGroupVO ag1 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO ag2 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(ag1.getUuid()).thenReturn("uuid-1");
|
||||
Mockito.when(ag1.getName()).thenReturn("affinity-group-1");
|
||||
Mockito.when(ag2.getUuid()).thenReturn("uuid-2");
|
||||
Mockito.when(ag2.getName()).thenReturn("affinity-group-2");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, CONTROL.name()))
|
||||
.thenReturn(Arrays.asList(1L, 2L));
|
||||
Mockito.when(affinityGroupDao.findById(1L)).thenReturn(ag1);
|
||||
Mockito.when(affinityGroupDao.findById(2L)).thenReturn(ag2);
|
||||
|
||||
kubernetesClusterManager.setAffinityGroupResponseForNodeType(response, clusterId, CONTROL.name());
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(clusterId, CONTROL.name());
|
||||
Mockito.verify(affinityGroupDao).findById(1L);
|
||||
Mockito.verify(affinityGroupDao).findById(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupResponseForNodeTypeWorker() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
AffinityGroupVO ag = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(ag.getUuid()).thenReturn("worker-uuid");
|
||||
Mockito.when(ag.getName()).thenReturn("worker-affinity");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(ag);
|
||||
|
||||
kubernetesClusterManager.setAffinityGroupResponseForNodeType(response, clusterId, WORKER.name());
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(clusterId, WORKER.name());
|
||||
Mockito.verify(affinityGroupDao).findById(10L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupResponseForNodeTypeEtcd() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
AffinityGroupVO ag = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(ag.getUuid()).thenReturn("etcd-uuid");
|
||||
Mockito.when(ag.getName()).thenReturn("etcd-affinity");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, ETCD.name()))
|
||||
.thenReturn(Arrays.asList(20L));
|
||||
Mockito.when(affinityGroupDao.findById(20L)).thenReturn(ag);
|
||||
|
||||
kubernetesClusterManager.setAffinityGroupResponseForNodeType(response, clusterId, ETCD.name());
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(clusterId, ETCD.name());
|
||||
Mockito.verify(affinityGroupDao).findById(20L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupResponseForNodeTypeEmptyList() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, CONTROL.name()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
kubernetesClusterManager.setAffinityGroupResponseForNodeType(response, clusterId, CONTROL.name());
|
||||
|
||||
Mockito.verify(affinityGroupDao, Mockito.never()).findById(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupResponseForNodeTypeNullList() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, ETCD.name()))
|
||||
.thenReturn(null);
|
||||
|
||||
kubernetesClusterManager.setAffinityGroupResponseForNodeType(response, clusterId, ETCD.name());
|
||||
|
||||
Mockito.verify(affinityGroupDao, Mockito.never()).findById(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAffinityGroupResponseForNodeTypeNullAffinityGroup() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
AffinityGroupVO ag1 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(ag1.getUuid()).thenReturn("uuid-1");
|
||||
Mockito.when(ag1.getName()).thenReturn("affinity-group-1");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(clusterId, CONTROL.name()))
|
||||
.thenReturn(Arrays.asList(1L, 2L));
|
||||
Mockito.when(affinityGroupDao.findById(1L)).thenReturn(ag1);
|
||||
Mockito.when(affinityGroupDao.findById(2L)).thenReturn(null);
|
||||
|
||||
kubernetesClusterManager.setAffinityGroupResponseForNodeType(response, clusterId, CONTROL.name());
|
||||
|
||||
Mockito.verify(affinityGroupDao).findById(1L);
|
||||
Mockito.verify(affinityGroupDao).findById(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNodeTypeAffinityGroupResponse() {
|
||||
KubernetesClusterResponse response = new KubernetesClusterResponse();
|
||||
long clusterId = 1L;
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(Mockito.eq(clusterId), Mockito.anyString()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
kubernetesClusterManager.setNodeTypeAffinityGroupResponse(response, clusterId);
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(clusterId, CONTROL.name());
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(clusterId, WORKER.name());
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(clusterId, ETCD.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsNoAffinityGroups() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
List<Long> nodeIds = Arrays.asList(100L, 101L);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(nodeIds, cluster);
|
||||
|
||||
Mockito.verify(kubernetesClusterVmMapDao, Mockito.never()).listByClusterIdAndVmType(Mockito.anyLong(), Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsNullAffinityGroups() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
List<Long> nodeIds = Arrays.asList(100L, 101L);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(null);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(nodeIds, cluster);
|
||||
|
||||
Mockito.verify(kubernetesClusterVmMapDao, Mockito.never()).listByClusterIdAndVmType(Mockito.anyLong(), Mockito.any());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateNodeAffinityGroupsAntiAffinityNewNodeOnExistingHost() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
Long existingWorkerVmId = 200L;
|
||||
Long sharedHostId = 1000L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getType()).thenReturn("host anti-affinity");
|
||||
Mockito.when(affinityGroup.getName()).thenReturn("anti-affinity-group");
|
||||
|
||||
VMInstanceVO newNode = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode.getHostId()).thenReturn(sharedHostId);
|
||||
Mockito.when(newNode.getInstanceName()).thenReturn("new-node-vm");
|
||||
|
||||
VMInstanceVO existingWorkerVm = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(existingWorkerVm.getHostId()).thenReturn(sharedHostId);
|
||||
|
||||
KubernetesClusterVmMapVO workerVmMap = Mockito.mock(KubernetesClusterVmMapVO.class);
|
||||
Mockito.when(workerVmMap.getVmId()).thenReturn(existingWorkerVmId);
|
||||
|
||||
HostVO host = Mockito.mock(HostVO.class);
|
||||
Mockito.when(host.getName()).thenReturn("host-1");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Arrays.asList(workerVmMap));
|
||||
Mockito.when(vmInstanceDao.findById(existingWorkerVmId)).thenReturn(existingWorkerVm);
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(newNode);
|
||||
Mockito.when(hostDao.findById(sharedHostId)).thenReturn(host);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsAntiAffinityNewNodeOnDifferentHost() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.lenient().when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
Long existingWorkerVmId = 200L;
|
||||
Long existingHostId = 1000L;
|
||||
Long newNodeHostId = 1001L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getType()).thenReturn("host anti-affinity");
|
||||
Mockito.lenient().when(affinityGroup.getName()).thenReturn("anti-affinity-group");
|
||||
|
||||
VMInstanceVO newNode = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode.getHostId()).thenReturn(newNodeHostId);
|
||||
|
||||
VMInstanceVO existingWorkerVm = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(existingWorkerVm.getHostId()).thenReturn(existingHostId);
|
||||
|
||||
KubernetesClusterVmMapVO workerVmMap = Mockito.mock(KubernetesClusterVmMapVO.class);
|
||||
Mockito.when(workerVmMap.getVmId()).thenReturn(existingWorkerVmId);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Arrays.asList(workerVmMap));
|
||||
Mockito.when(vmInstanceDao.findById(existingWorkerVmId)).thenReturn(existingWorkerVm);
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(newNode);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsAffinityNewNodeOnSameHost() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.lenient().when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
Long existingWorkerVmId = 200L;
|
||||
Long sharedHostId = 1000L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getType()).thenReturn("host affinity");
|
||||
Mockito.lenient().when(affinityGroup.getName()).thenReturn("affinity-group");
|
||||
|
||||
VMInstanceVO newNode = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode.getHostId()).thenReturn(sharedHostId);
|
||||
|
||||
VMInstanceVO existingWorkerVm = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(existingWorkerVm.getHostId()).thenReturn(sharedHostId);
|
||||
|
||||
KubernetesClusterVmMapVO workerVmMap = Mockito.mock(KubernetesClusterVmMapVO.class);
|
||||
Mockito.when(workerVmMap.getVmId()).thenReturn(existingWorkerVmId);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Arrays.asList(workerVmMap));
|
||||
Mockito.when(vmInstanceDao.findById(existingWorkerVmId)).thenReturn(existingWorkerVm);
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(newNode);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateNodeAffinityGroupsAffinityNewNodeOnDifferentHost() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
Long existingWorkerVmId = 200L;
|
||||
Long existingHostId = 1000L;
|
||||
Long newNodeHostId = 1001L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getType()).thenReturn("host affinity");
|
||||
Mockito.when(affinityGroup.getName()).thenReturn("affinity-group");
|
||||
|
||||
VMInstanceVO newNode = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode.getHostId()).thenReturn(newNodeHostId);
|
||||
Mockito.when(newNode.getInstanceName()).thenReturn("new-node-vm");
|
||||
|
||||
VMInstanceVO existingWorkerVm = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(existingWorkerVm.getHostId()).thenReturn(existingHostId);
|
||||
|
||||
KubernetesClusterVmMapVO workerVmMap = Mockito.mock(KubernetesClusterVmMapVO.class);
|
||||
Mockito.when(workerVmMap.getVmId()).thenReturn(existingWorkerVmId);
|
||||
|
||||
HostVO newHost = Mockito.mock(HostVO.class);
|
||||
Mockito.when(newHost.getName()).thenReturn("host-2");
|
||||
|
||||
HostVO existingHost = Mockito.mock(HostVO.class);
|
||||
Mockito.when(existingHost.getName()).thenReturn("host-1");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Arrays.asList(workerVmMap));
|
||||
Mockito.when(vmInstanceDao.findById(existingWorkerVmId)).thenReturn(existingWorkerVm);
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(newNode);
|
||||
Mockito.when(hostDao.findById(newNodeHostId)).thenReturn(newHost);
|
||||
Mockito.when(hostDao.findById(existingHostId)).thenReturn(existingHost);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateNodeAffinityGroupsAntiAffinityMultipleNewNodesOnSameHost() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId1 = 100L;
|
||||
Long newNodeId2 = 101L;
|
||||
Long sharedHostId = 1000L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.lenient().when(affinityGroup.getType()).thenReturn("host anti-affinity");
|
||||
Mockito.when(affinityGroup.getName()).thenReturn("anti-affinity-group");
|
||||
|
||||
VMInstanceVO newNode1 = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode1.getHostId()).thenReturn(sharedHostId);
|
||||
Mockito.lenient().when(newNode1.getInstanceName()).thenReturn("new-node-vm-1");
|
||||
|
||||
VMInstanceVO newNode2 = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode2.getHostId()).thenReturn(sharedHostId);
|
||||
Mockito.when(newNode2.getInstanceName()).thenReturn("new-node-vm-2");
|
||||
|
||||
HostVO host = Mockito.mock(HostVO.class);
|
||||
Mockito.when(host.getName()).thenReturn("host-1");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId1)).thenReturn(newNode1);
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId2)).thenReturn(newNode2);
|
||||
Mockito.when(hostDao.findById(sharedHostId)).thenReturn(host);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId1, newNodeId2), cluster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsAntiAffinityMultipleNewNodesOnDifferentHosts() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.lenient().when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId1 = 100L;
|
||||
Long newNodeId2 = 101L;
|
||||
Long hostId1 = 1000L;
|
||||
Long hostId2 = 1001L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.lenient().when(affinityGroup.getType()).thenReturn("host anti-affinity");
|
||||
Mockito.lenient().when(affinityGroup.getName()).thenReturn("anti-affinity-group");
|
||||
|
||||
VMInstanceVO newNode1 = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode1.getHostId()).thenReturn(hostId1);
|
||||
|
||||
VMInstanceVO newNode2 = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode2.getHostId()).thenReturn(hostId2);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId1)).thenReturn(newNode1);
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId2)).thenReturn(newNode2);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId1, newNodeId2), cluster);
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsNodeWithNullHost() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.lenient().when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.lenient().when(affinityGroup.getType()).thenReturn("host anti-affinity");
|
||||
Mockito.lenient().when(affinityGroup.getName()).thenReturn("anti-affinity-group");
|
||||
|
||||
VMInstanceVO newNode = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode.getHostId()).thenReturn(null);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(newNode);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
|
||||
Mockito.verify(vmInstanceDao, Mockito.atLeastOnce()).findById(newNodeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsNullNode() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.lenient().when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.lenient().when(affinityGroup.getType()).thenReturn("host anti-affinity");
|
||||
Mockito.lenient().when(affinityGroup.getName()).thenReturn("anti-affinity-group");
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(null);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
|
||||
Mockito.verify(vmInstanceDao, Mockito.atLeastOnce()).findById(newNodeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateNodeAffinityGroupsAffinityNoExistingWorkers() {
|
||||
KubernetesCluster cluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(cluster.getId()).thenReturn(1L);
|
||||
Mockito.lenient().when(cluster.getName()).thenReturn("test-cluster");
|
||||
|
||||
Long newNodeId = 100L;
|
||||
Long newNodeHostId = 1000L;
|
||||
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.lenient().when(affinityGroup.getType()).thenReturn("host affinity");
|
||||
Mockito.lenient().when(affinityGroup.getName()).thenReturn("affinity-group");
|
||||
|
||||
VMInstanceVO newNode = Mockito.mock(VMInstanceVO.class);
|
||||
Mockito.when(newNode.getHostId()).thenReturn(newNodeHostId);
|
||||
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name()))
|
||||
.thenReturn(Arrays.asList(10L));
|
||||
Mockito.when(affinityGroupDao.findById(10L)).thenReturn(affinityGroup);
|
||||
Mockito.when(kubernetesClusterVmMapDao.listByClusterIdAndVmType(1L, WORKER))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Mockito.when(vmInstanceDao.findById(newNodeId)).thenReturn(newNode);
|
||||
|
||||
kubernetesClusterManager.validateNodeAffinityGroups(Arrays.asList(newNodeId), cluster);
|
||||
|
||||
Mockito.verify(kubernetesClusterAffinityGroupMapDao).listAffinityGroupIdsByClusterIdAndNodeType(1L, WORKER.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@
|
|||
package com.cloud.kubernetes.cluster;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
|
|
@ -24,11 +33,16 @@ import org.mockito.Mock;
|
|||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class KubernetesServiceHelperImplTest {
|
||||
|
|
@ -36,6 +50,10 @@ public class KubernetesServiceHelperImplTest {
|
|||
KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
|
||||
@Mock
|
||||
KubernetesClusterDao kubernetesClusterDao;
|
||||
@Mock
|
||||
AffinityGroupDao affinityGroupDao;
|
||||
@Mock
|
||||
ServiceOfferingDao serviceOfferingDao;
|
||||
|
||||
@InjectMocks
|
||||
KubernetesServiceHelperImpl kubernetesServiceHelper = new KubernetesServiceHelperImpl();
|
||||
|
|
@ -84,4 +102,302 @@ public class KubernetesServiceHelperImplTest {
|
|||
Mockito.when(kubernetesCluster.getClusterType()).thenReturn(KubernetesCluster.ClusterType.ExternalManaged);
|
||||
kubernetesServiceHelper.checkVmCanBeDestroyed(vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidNodeTypeEmptyNodeType() {
|
||||
Assert.assertFalse(kubernetesServiceHelper.isValidNodeType(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidNodeTypeInvalidNodeType() {
|
||||
Assert.assertFalse(kubernetesServiceHelper.isValidNodeType("invalidNodeType"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidNodeTypeValidNodeTypeLowercase() {
|
||||
String nodeType = KubernetesClusterNodeType.WORKER.name().toLowerCase();
|
||||
Assert.assertTrue(kubernetesServiceHelper.isValidNodeType(nodeType));
|
||||
}
|
||||
|
||||
private Map<String, String> createServiceOfferingMapEntry(KubernetesClusterNodeType nodeType, String offeringUuid) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(VmDetailConstants.CKS_NODE_TYPE, nodeType.name().toLowerCase());
|
||||
map.put(VmDetailConstants.OFFERING, offeringUuid);
|
||||
return map;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingNodeTypeMap() {
|
||||
String workerOfferingUuid = UUID.randomUUID().toString();
|
||||
String controlOfferingUuid = UUID.randomUUID().toString();
|
||||
|
||||
ServiceOfferingVO workerOffering = Mockito.mock(ServiceOfferingVO.class);
|
||||
Mockito.when(workerOffering.getId()).thenReturn(1L);
|
||||
Mockito.when(serviceOfferingDao.findByUuid(workerOfferingUuid)).thenReturn(workerOffering);
|
||||
|
||||
ServiceOfferingVO controlOffering = Mockito.mock(ServiceOfferingVO.class);
|
||||
Mockito.when(controlOffering.getId()).thenReturn(2L);
|
||||
Mockito.when(serviceOfferingDao.findByUuid(controlOfferingUuid)).thenReturn(controlOffering);
|
||||
|
||||
Map<String, Map<String, String>> serviceOfferingNodeTypeMap = new HashMap<>();
|
||||
serviceOfferingNodeTypeMap.put("map1", createServiceOfferingMapEntry(KubernetesClusterNodeType.WORKER, workerOfferingUuid));
|
||||
serviceOfferingNodeTypeMap.put("map2", createServiceOfferingMapEntry(KubernetesClusterNodeType.CONTROL, controlOfferingUuid));
|
||||
|
||||
Map<String, Long> result = kubernetesServiceHelper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap);
|
||||
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertTrue(result.containsKey(KubernetesClusterNodeType.WORKER.name()));
|
||||
Assert.assertTrue(result.containsKey(KubernetesClusterNodeType.CONTROL.name()));
|
||||
Assert.assertEquals(Long.valueOf(1L), result.get(KubernetesClusterNodeType.WORKER.name()));
|
||||
Assert.assertEquals(Long.valueOf(2L), result.get(KubernetesClusterNodeType.CONTROL.name()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingNodeTypeMapNullMap() {
|
||||
Map<String, Long> result = kubernetesServiceHelper.getServiceOfferingNodeTypeMap(null);
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetServiceOfferingNodeTypeMapEtcdNodes() {
|
||||
String etcdOfferingUuid = UUID.randomUUID().toString();
|
||||
|
||||
ServiceOfferingVO etcdOffering = Mockito.mock(ServiceOfferingVO.class);
|
||||
Mockito.when(etcdOffering.getId()).thenReturn(3L);
|
||||
Mockito.when(serviceOfferingDao.findByUuid(etcdOfferingUuid)).thenReturn(etcdOffering);
|
||||
|
||||
Map<String, Map<String, String>> serviceOfferingNodeTypeMap = new HashMap<>();
|
||||
serviceOfferingNodeTypeMap.put("map1", createServiceOfferingMapEntry(KubernetesClusterNodeType.ETCD, etcdOfferingUuid));
|
||||
|
||||
Map<String, Long> result = kubernetesServiceHelper.getServiceOfferingNodeTypeMap(serviceOfferingNodeTypeMap);
|
||||
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(1, result.size());
|
||||
Assert.assertTrue(result.containsKey(KubernetesClusterNodeType.ETCD.name()));
|
||||
Assert.assertEquals(Long.valueOf(3L), result.get(KubernetesClusterNodeType.ETCD.name()));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeOfferingEntryCompletenessInvalidParameters() {
|
||||
kubernetesServiceHelper.checkNodeTypeOfferingEntryCompleteness(KubernetesClusterNodeType.WORKER.name(), null);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeOfferingEntryValuesInvalidNodeType() {
|
||||
ServiceOfferingVO offering = Mockito.mock(ServiceOfferingVO.class);
|
||||
kubernetesServiceHelper.checkNodeTypeOfferingEntryValues("invalidNodeTypeName", offering, "some-uuid");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeOfferingEntryValuesEmptyOffering() {
|
||||
kubernetesServiceHelper.checkNodeTypeOfferingEntryValues(KubernetesClusterNodeType.WORKER.name(), null, "some-uuid");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeAffinityGroupEntryCompletenessBlankNodeType() {
|
||||
kubernetesServiceHelper.checkNodeTypeAffinityGroupEntryCompleteness("", "affinity-group-uuid");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeAffinityGroupEntryCompletenessBlankAffinityGroupUuid() {
|
||||
kubernetesServiceHelper.checkNodeTypeAffinityGroupEntryCompleteness("control", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckNodeTypeAffinityGroupEntryCompletenessValid() {
|
||||
kubernetesServiceHelper.checkNodeTypeAffinityGroupEntryCompleteness("control", "affinity-group-uuid");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCheckNodeTypeAffinityGroupEntryNodeTypeInvalid() {
|
||||
kubernetesServiceHelper.checkNodeTypeAffinityGroupEntryNodeType("invalid-node-type");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckNodeTypeAffinityGroupEntryNodeTypeValid() {
|
||||
kubernetesServiceHelper.checkNodeTypeAffinityGroupEntryNodeType("control");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateAffinityGroupUuidAndGetIdBlank() {
|
||||
kubernetesServiceHelper.validateAffinityGroupUuidAndGetId("");
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateAffinityGroupUuidAndGetIdNotFound() {
|
||||
Mockito.when(affinityGroupDao.findByUuid("non-existent-uuid")).thenReturn(null);
|
||||
kubernetesServiceHelper.validateAffinityGroupUuidAndGetId("non-existent-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAffinityGroupUuidAndGetIdValid() {
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getId()).thenReturn(100L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("valid-uuid")).thenReturn(affinityGroup);
|
||||
Long result = kubernetesServiceHelper.validateAffinityGroupUuidAndGetId("valid-uuid");
|
||||
Assert.assertEquals(Long.valueOf(100L), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAndGetAffinityGroupIdsSingleUuid() {
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getId()).thenReturn(1L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid1")).thenReturn(affinityGroup);
|
||||
|
||||
List<Long> result = kubernetesServiceHelper.validateAndGetAffinityGroupIds("uuid1");
|
||||
Assert.assertEquals(1, result.size());
|
||||
Assert.assertEquals(Long.valueOf(1L), result.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAndGetAffinityGroupIdsMultipleUuids() {
|
||||
AffinityGroupVO affinityGroup1 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO affinityGroup2 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO affinityGroup3 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup1.getId()).thenReturn(1L);
|
||||
Mockito.when(affinityGroup2.getId()).thenReturn(2L);
|
||||
Mockito.when(affinityGroup3.getId()).thenReturn(3L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid1")).thenReturn(affinityGroup1);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid2")).thenReturn(affinityGroup2);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid3")).thenReturn(affinityGroup3);
|
||||
|
||||
List<Long> result = kubernetesServiceHelper.validateAndGetAffinityGroupIds("uuid1,uuid2,uuid3");
|
||||
Assert.assertEquals(3, result.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L, 3L), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateAndGetAffinityGroupIdsWithSpaces() {
|
||||
AffinityGroupVO affinityGroup1 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO affinityGroup2 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup1.getId()).thenReturn(1L);
|
||||
Mockito.when(affinityGroup2.getId()).thenReturn(2L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid1")).thenReturn(affinityGroup1);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid2")).thenReturn(affinityGroup2);
|
||||
|
||||
List<Long> result = kubernetesServiceHelper.validateAndGetAffinityGroupIds(" uuid1 , uuid2 ");
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L), result);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testValidateAndGetAffinityGroupIdsOneInvalid() {
|
||||
AffinityGroupVO affinityGroup1 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid1")).thenReturn(affinityGroup1);
|
||||
Mockito.when(affinityGroupDao.findByUuid("invalid-uuid")).thenReturn(null);
|
||||
|
||||
kubernetesServiceHelper.validateAndGetAffinityGroupIds("uuid1,invalid-uuid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNodeTypeAffinityGroupEntry() {
|
||||
Map<String, List<Long>> mapping = new HashMap<>();
|
||||
kubernetesServiceHelper.addNodeTypeAffinityGroupEntry("control", Arrays.asList(1L, 2L), mapping);
|
||||
Assert.assertEquals(1, mapping.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L), mapping.get("CONTROL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNodeTypeAffinityGroupEntryAndAddToMappingIfValidEmptyEntry() {
|
||||
Map<String, List<Long>> mapping = new HashMap<>();
|
||||
kubernetesServiceHelper.processNodeTypeAffinityGroupEntryAndAddToMappingIfValid(new HashMap<>(), mapping);
|
||||
Assert.assertTrue(mapping.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNodeTypeAffinityGroupEntryAndAddToMappingIfValidValidEntry() {
|
||||
AffinityGroupVO affinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup.getId()).thenReturn(100L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("affinity-group-uuid")).thenReturn(affinityGroup);
|
||||
|
||||
Map<String, String> entry = new HashMap<>();
|
||||
entry.put(VmDetailConstants.CKS_NODE_TYPE, "control");
|
||||
entry.put(VmDetailConstants.AFFINITY_GROUP, "affinity-group-uuid");
|
||||
|
||||
Map<String, List<Long>> mapping = new HashMap<>();
|
||||
kubernetesServiceHelper.processNodeTypeAffinityGroupEntryAndAddToMappingIfValid(entry, mapping);
|
||||
Assert.assertEquals(1, mapping.size());
|
||||
Assert.assertEquals(Arrays.asList(100L), mapping.get("CONTROL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNodeTypeAffinityGroupEntryAndAddToMappingIfValidMultipleUuids() {
|
||||
AffinityGroupVO affinityGroup1 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO affinityGroup2 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(affinityGroup1.getId()).thenReturn(1L);
|
||||
Mockito.when(affinityGroup2.getId()).thenReturn(2L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid1")).thenReturn(affinityGroup1);
|
||||
Mockito.when(affinityGroupDao.findByUuid("uuid2")).thenReturn(affinityGroup2);
|
||||
|
||||
Map<String, String> entry = new HashMap<>();
|
||||
entry.put(VmDetailConstants.CKS_NODE_TYPE, "worker");
|
||||
entry.put(VmDetailConstants.AFFINITY_GROUP, "uuid1,uuid2");
|
||||
|
||||
Map<String, List<Long>> mapping = new HashMap<>();
|
||||
kubernetesServiceHelper.processNodeTypeAffinityGroupEntryAndAddToMappingIfValid(entry, mapping);
|
||||
Assert.assertEquals(1, mapping.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L), mapping.get("WORKER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAffinityGroupNodeTypeMapEmptyMap() {
|
||||
Map<String, List<Long>> result = kubernetesServiceHelper.getAffinityGroupNodeTypeMap(null);
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
|
||||
result = kubernetesServiceHelper.getAffinityGroupNodeTypeMap(new HashMap<>());
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAffinityGroupNodeTypeMapValidEntries() {
|
||||
AffinityGroupVO controlAffinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(controlAffinityGroup.getId()).thenReturn(100L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("control-affinity-uuid")).thenReturn(controlAffinityGroup);
|
||||
|
||||
AffinityGroupVO workerAffinityGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(workerAffinityGroup.getId()).thenReturn(200L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("worker-affinity-uuid")).thenReturn(workerAffinityGroup);
|
||||
|
||||
Map<String, Map<String, String>> affinityGroupNodeTypeMap = new HashMap<>();
|
||||
|
||||
Map<String, String> controlEntry = new HashMap<>();
|
||||
controlEntry.put(VmDetailConstants.CKS_NODE_TYPE, "control");
|
||||
controlEntry.put(VmDetailConstants.AFFINITY_GROUP, "control-affinity-uuid");
|
||||
affinityGroupNodeTypeMap.put("0", controlEntry);
|
||||
|
||||
Map<String, String> workerEntry = new HashMap<>();
|
||||
workerEntry.put(VmDetailConstants.CKS_NODE_TYPE, "worker");
|
||||
workerEntry.put(VmDetailConstants.AFFINITY_GROUP, "worker-affinity-uuid");
|
||||
affinityGroupNodeTypeMap.put("1", workerEntry);
|
||||
|
||||
Map<String, List<Long>> result = kubernetesServiceHelper.getAffinityGroupNodeTypeMap(affinityGroupNodeTypeMap);
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertEquals(Arrays.asList(100L), result.get("CONTROL"));
|
||||
Assert.assertEquals(Arrays.asList(200L), result.get("WORKER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAffinityGroupNodeTypeMapMultipleIdsPerNodeType() {
|
||||
AffinityGroupVO ag1 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO ag2 = Mockito.mock(AffinityGroupVO.class);
|
||||
AffinityGroupVO ag3 = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(ag1.getId()).thenReturn(1L);
|
||||
Mockito.when(ag2.getId()).thenReturn(2L);
|
||||
Mockito.when(ag3.getId()).thenReturn(3L);
|
||||
Mockito.when(affinityGroupDao.findByUuid("ag1")).thenReturn(ag1);
|
||||
Mockito.when(affinityGroupDao.findByUuid("ag2")).thenReturn(ag2);
|
||||
Mockito.when(affinityGroupDao.findByUuid("ag3")).thenReturn(ag3);
|
||||
|
||||
Map<String, Map<String, String>> affinityGroupNodeTypeMap = new HashMap<>();
|
||||
|
||||
Map<String, String> controlEntry = new HashMap<>();
|
||||
controlEntry.put(VmDetailConstants.CKS_NODE_TYPE, "control");
|
||||
controlEntry.put(VmDetailConstants.AFFINITY_GROUP, "ag1,ag2,ag3");
|
||||
affinityGroupNodeTypeMap.put("0", controlEntry);
|
||||
|
||||
Map<String, List<Long>> result = kubernetesServiceHelper.getAffinityGroupNodeTypeMap(affinityGroupNodeTypeMap);
|
||||
Assert.assertEquals(1, result.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L, 3L), result.get("CONTROL"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@
|
|||
// under the License.
|
||||
package com.cloud.kubernetes.cluster.actionworkers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.cloudstack.affinity.AffinityGroupVO;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -30,6 +36,8 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||
import com.cloud.kubernetes.cluster.KubernetesCluster;
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterDetailsVO;
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterManagerImpl;
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterAffinityGroupMapDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDetailsDao;
|
||||
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
|
||||
|
|
@ -60,6 +68,12 @@ public class KubernetesClusterActionWorkerTest {
|
|||
@Mock
|
||||
IPAddressDao ipAddressDao;
|
||||
|
||||
@Mock
|
||||
KubernetesClusterAffinityGroupMapDao kubernetesClusterAffinityGroupMapDao;
|
||||
|
||||
@Mock
|
||||
AffinityGroupDao affinityGroupDao;
|
||||
|
||||
KubernetesClusterActionWorker actionWorker = null;
|
||||
|
||||
final static Long DEFAULT_ID = 1L;
|
||||
|
|
@ -70,10 +84,12 @@ public class KubernetesClusterActionWorkerTest {
|
|||
kubernetesClusterManager.kubernetesSupportedVersionDao = kubernetesSupportedVersionDao;
|
||||
kubernetesClusterManager.kubernetesClusterDetailsDao = kubernetesClusterDetailsDao;
|
||||
kubernetesClusterManager.kubernetesClusterVmMapDao = kubernetesClusterVmMapDao;
|
||||
kubernetesClusterManager.kubernetesClusterAffinityGroupMapDao = kubernetesClusterAffinityGroupMapDao;
|
||||
KubernetesCluster kubernetesCluster = Mockito.mock(KubernetesCluster.class);
|
||||
Mockito.when(kubernetesCluster.getId()).thenReturn(DEFAULT_ID);
|
||||
actionWorker = new KubernetesClusterActionWorker(kubernetesCluster, kubernetesClusterManager);
|
||||
actionWorker.ipAddressDao = ipAddressDao;
|
||||
actionWorker.affinityGroupDao = affinityGroupDao;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -130,4 +146,87 @@ public class KubernetesClusterActionWorkerTest {
|
|||
IpAddress result = actionWorker.getVpcTierKubernetesPublicIp(mockNetworkForGetVpcTierKubernetesPublicIpTest());
|
||||
Assert.assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAffinityGroupIdsForNodeTypeReturnsIds() {
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(DEFAULT_ID, "CONTROL"))
|
||||
.thenReturn(Arrays.asList(1L, 2L));
|
||||
|
||||
List<Long> result = actionWorker.getAffinityGroupIdsForNodeType(KubernetesClusterNodeType.CONTROL);
|
||||
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertTrue(result.containsAll(Arrays.asList(1L, 2L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAffinityGroupIdsForNodeTypeReturnsEmptyList() {
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(DEFAULT_ID, "WORKER"))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
List<Long> result = actionWorker.getAffinityGroupIdsForNodeType(KubernetesClusterNodeType.WORKER);
|
||||
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMergedAffinityGroupIdsWithExplicitDedication() {
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(DEFAULT_ID, "CONTROL"))
|
||||
.thenReturn(new ArrayList<>(Arrays.asList(1L)));
|
||||
|
||||
AffinityGroupVO explicitGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(explicitGroup.getId()).thenReturn(99L);
|
||||
Mockito.when(affinityGroupDao.findByAccountAndType(Mockito.anyLong(), Mockito.eq("ExplicitDedication")))
|
||||
.thenReturn(explicitGroup);
|
||||
|
||||
List<Long> result = actionWorker.getMergedAffinityGroupIds(KubernetesClusterNodeType.CONTROL, 1L, 1L);
|
||||
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertTrue(result.contains(1L));
|
||||
Assert.assertTrue(result.contains(99L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMergedAffinityGroupIdsNoExplicitDedication() {
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(DEFAULT_ID, "WORKER"))
|
||||
.thenReturn(new ArrayList<>(Arrays.asList(1L, 2L)));
|
||||
Mockito.when(affinityGroupDao.findByAccountAndType(Mockito.anyLong(), Mockito.eq("ExplicitDedication")))
|
||||
.thenReturn(null);
|
||||
Mockito.when(affinityGroupDao.findDomainLevelGroupByType(Mockito.anyLong(), Mockito.eq("ExplicitDedication")))
|
||||
.thenReturn(null);
|
||||
|
||||
List<Long> result = actionWorker.getMergedAffinityGroupIds(KubernetesClusterNodeType.WORKER, 1L, 1L);
|
||||
|
||||
Assert.assertEquals(2, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMergedAffinityGroupIdsReturnsNullWhenEmpty() {
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(DEFAULT_ID, "ETCD"))
|
||||
.thenReturn(new ArrayList<>());
|
||||
Mockito.when(affinityGroupDao.findByAccountAndType(Mockito.anyLong(), Mockito.anyString()))
|
||||
.thenReturn(null);
|
||||
Mockito.when(affinityGroupDao.findDomainLevelGroupByType(Mockito.anyLong(), Mockito.anyString()))
|
||||
.thenReturn(null);
|
||||
|
||||
List<Long> result = actionWorker.getMergedAffinityGroupIds(KubernetesClusterNodeType.ETCD, 1L, 1L);
|
||||
|
||||
Assert.assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMergedAffinityGroupIdsExplicitDedicationAlreadyInList() {
|
||||
Mockito.when(kubernetesClusterAffinityGroupMapDao.listAffinityGroupIdsByClusterIdAndNodeType(DEFAULT_ID, "CONTROL"))
|
||||
.thenReturn(new ArrayList<>(Arrays.asList(99L, 2L)));
|
||||
|
||||
AffinityGroupVO explicitGroup = Mockito.mock(AffinityGroupVO.class);
|
||||
Mockito.when(explicitGroup.getId()).thenReturn(99L);
|
||||
Mockito.when(affinityGroupDao.findByAccountAndType(Mockito.anyLong(), Mockito.eq("ExplicitDedication")))
|
||||
.thenReturn(explicitGroup);
|
||||
|
||||
List<Long> result = actionWorker.getMergedAffinityGroupIds(KubernetesClusterNodeType.CONTROL, 1L, 1L);
|
||||
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertTrue(result.contains(99L));
|
||||
Assert.assertTrue(result.contains(2L));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,21 +25,30 @@ import javax.inject.Inject;
|
|||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import org.apache.cloudstack.acl.RolePermissionEntity;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.ListUserKeyRulesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeyCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||
|
|
@ -119,7 +128,7 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String[] createApiKeyAndSecretKey(RegisterUserKeyCmd arg0) {
|
||||
public ApiKeyPair createApiKeyAndSecretKey(RegisterUserKeysCmd arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
|
@ -401,7 +410,7 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Pair<User, Account> findUserByApiKey(String arg0) {
|
||||
public Ternary<User, Account, ApiKeyPair> findUserByApiKey(String arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
|
@ -466,6 +475,10 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateCallingUserHasAccessToDesiredUser(Long userId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long finalizeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) {
|
||||
// TODO Auto-generated method stub
|
||||
|
|
@ -503,10 +516,23 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, Map<String, String>> getKeys(Long userId) {
|
||||
public ListResponse<ApiKeyPairResponse> listKeys(ListUserKeysCmd cmd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApiKeyPairPermission> listKeyRules(ListUserKeyRulesCmd cmd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteApiKey(DeleteUserKeysCmd cmd) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteApiKey(ApiKeyPair id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserTwoFactorAuthenticator> listUserTwoFactorAuthenticationProviders() {
|
||||
return null;
|
||||
|
|
@ -517,6 +543,31 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiKeyPair getLatestUserKeyPair(Long userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiKeyPair getKeyPairById(Long id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiKeyPair getKeyPairByApiKey(String apiKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessingApiKey(BaseCmd cmd) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionEntity> getAllKeypairPermissions(String apiKey) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(User user, ControlledEntity entity)
|
||||
throws PermissionDeniedException {
|
||||
|
|
@ -538,7 +589,7 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void checkApiAccess(Account account, String command) throws PermissionDeniedException {
|
||||
public void checkApiAccess(Account account, String command, String apiKey) throws PermissionDeniedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -549,4 +600,12 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
@Override
|
||||
public void verifyCallerPrivilegeForUserOrAccountOperations(Account userAccount) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyCallerPrivilegeForUserOrAccountOperations(User user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCallerRoleTypeAllowedForUserOrAccountOperations(Account userAccount, User user) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.apache.cloudstack.network.tungsten.service;
|
|||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.configuration.ConfigurationManager;
|
||||
import com.cloud.dc.DataCenter;
|
||||
|
|
@ -114,6 +115,7 @@ import net.juniper.tungsten.api.types.TagType;
|
|||
import net.juniper.tungsten.api.types.VirtualMachine;
|
||||
import net.juniper.tungsten.api.types.VirtualMachineInterface;
|
||||
import net.juniper.tungsten.api.types.VirtualNetwork;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairVO;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
|
|
@ -1182,9 +1184,10 @@ public class TungstenServiceImpl extends ManagerBase implements TungstenService
|
|||
int listenerPort = NetUtils.HTTPS_PORT;
|
||||
|
||||
User callerUser = accountMgr.getActiveUser(CallContext.current().getCallingUserId());
|
||||
String apiKey = callerUser.getApiKey();
|
||||
String secretKey = callerUser.getSecretKey();
|
||||
if (apiKey != null && secretKey != null) {
|
||||
ApiKeyPairVO latestKeypair = ApiDBUtils.searchForLatestUserKeyPair(callerUser.getId());
|
||||
if (latestKeypair != null) {
|
||||
String apiKey = latestKeypair.getApiKey();
|
||||
String secretKey = latestKeypair.getSecretKey();
|
||||
String url;
|
||||
try {
|
||||
String data = "apiKey=" + URLEncoder.encode(apiKey, StandardCharsets.UTF_8.name()).replace("\\+", "%20") + "&command"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.network.tungsten.service;
|
||||
|
||||
import org.apache.cloudstack.acl.ApiKeyPairVO;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
|
@ -360,6 +361,10 @@ public class TungstenElementTest {
|
|||
when(lbStickinessPolicy.getMethodName()).thenReturn("AppCookie");
|
||||
List<Pair<String, String>> pairList = List.of(new Pair<>("cookieName", "cookieValue"));
|
||||
|
||||
ApiKeyPairVO latest = new ApiKeyPairVO();
|
||||
latest.setApiKey("apikey");
|
||||
latest.setSecretKey("secretkey");
|
||||
when(ApiDBUtils.searchForLatestUserKeyPair(Mockito.anyLong())).thenReturn(latest);
|
||||
when(lbStickinessPolicy.getParams()).thenReturn(pairList);
|
||||
when(loadBalancingRule1.getId()).thenReturn(1L);
|
||||
when(loadBalancingRule1.getState()).thenReturn(FirewallRule.State.Add);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public class S3ImageStoreDriverImpl extends BaseImageStoreDriverImpl {
|
|||
return new S3TO(imgStore.getId(),
|
||||
imgStore.getUuid(),
|
||||
details.get(ApiConstants.S3_ACCESS_KEY),
|
||||
details.get(ApiConstants.S3_SECRET_KEY),
|
||||
details.get(ApiConstants.SECRET_KEY),
|
||||
details.get(ApiConstants.S3_END_POINT),
|
||||
details.get(ApiConstants.S3_BUCKET_NAME),
|
||||
details.get(ApiConstants.S3_SIGNER),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue