blob: 6d44565d8234be4adfb34fcaee7d8c21be013e54 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.kubernetes.cluster.actionworkers;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.ca.CAManager;
import org.apache.cloudstack.config.ApiServiceConfiguration;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.kubernetes.cluster.KubernetesCluster;
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.KubernetesClusterDao;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterDetailsDao;
import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao;
import com.cloud.kubernetes.version.KubernetesSupportedVersion;
import com.cloud.kubernetes.version.dao.KubernetesSupportedVersionDao;
import com.cloud.network.IpAddress;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.Network.GuestType;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkService;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.vpc.VpcService;
import com.cloud.projects.ProjectService;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.LaunchPermissionDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.template.TemplateApiService;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.SSHKeyPairDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.utils.ssh.SshHelper;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.UserVmService;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
public class KubernetesClusterActionWorker {
public static final String CLUSTER_NODE_VM_USER = "cloud";
public static final int CLUSTER_API_PORT = 6443;
public static final int DEFAULT_SSH_PORT = 22;
public static final int CLUSTER_NODES_DEFAULT_START_SSH_PORT = 2222;
public static final int CLUSTER_NODES_DEFAULT_SSH_PORT_SG = DEFAULT_SSH_PORT;
public static final String CKS_CLUSTER_SECURITY_GROUP_NAME = "CKSSecurityGroup";
public static final String CKS_SECURITY_GROUP_DESCRIPTION = "Security group for CKS nodes";
protected static final Logger LOGGER = Logger.getLogger(KubernetesClusterActionWorker.class);
protected StateMachine2<KubernetesCluster.State, KubernetesCluster.Event, KubernetesCluster> _stateMachine = KubernetesCluster.State.getStateMachine();
@Inject
protected CAManager caManager;
@Inject
protected ConfigurationDao configurationDao;
@Inject
protected DataCenterDao dataCenterDao;
@Inject
protected AccountDao accountDao;
@Inject
protected IpAddressManager ipAddressManager;
@Inject
protected IPAddressDao ipAddressDao;
@Inject
protected NetworkOrchestrationService networkMgr;
@Inject
protected NetworkDao networkDao;
@Inject
protected NetworkModel networkModel;
@Inject
protected NetworkService networkService;
@Inject
protected ServiceOfferingDao serviceOfferingDao;
@Inject
protected SSHKeyPairDao sshKeyPairDao;
@Inject
protected VMTemplateDao templateDao;
@Inject
protected TemplateApiService templateService;
@Inject
protected UserVmDao userVmDao;
@Inject
protected UserVmDetailsDao userVmDetailsDao;
@Inject
protected UserVmService userVmService;
@Inject
protected VlanDao vlanDao;
@Inject
protected VirtualMachineManager itMgr;
@Inject
protected LaunchPermissionDao launchPermissionDao;
@Inject
public ProjectService projectService;
@Inject
public VpcService vpcService;
protected KubernetesClusterDao kubernetesClusterDao;
protected KubernetesClusterVmMapDao kubernetesClusterVmMapDao;
protected KubernetesClusterDetailsDao kubernetesClusterDetailsDao;
protected KubernetesSupportedVersionDao kubernetesSupportedVersionDao;
protected KubernetesCluster kubernetesCluster;
protected Account owner;
protected VirtualMachineTemplate clusterTemplate;
protected File sshKeyFile;
protected String publicIpAddress;
protected int sshPort;
protected final String deploySecretsScriptFilename = "deploy-cloudstack-secret";
protected final String deployProviderScriptFilename = "deploy-provider";
protected final String autoscaleScriptFilename = "autoscale-kube-cluster";
protected final String scriptPath = "/opt/bin/";
protected File deploySecretsScriptFile;
protected File deployProviderScriptFile;
protected File autoscaleScriptFile;
protected KubernetesClusterManagerImpl manager;
protected String[] keys;
protected KubernetesClusterActionWorker(final KubernetesCluster kubernetesCluster, final KubernetesClusterManagerImpl clusterManager) {
this.kubernetesCluster = kubernetesCluster;
this.kubernetesClusterDao = clusterManager.kubernetesClusterDao;
this.kubernetesClusterDetailsDao = clusterManager.kubernetesClusterDetailsDao;
this.kubernetesClusterVmMapDao = clusterManager.kubernetesClusterVmMapDao;
this.kubernetesSupportedVersionDao = clusterManager.kubernetesSupportedVersionDao;
this.manager = clusterManager;
}
protected void init() {
this.owner = accountDao.findById(kubernetesCluster.getAccountId());
long zoneId = this.kubernetesCluster.getZoneId();
long templateId = this.kubernetesCluster.getTemplateId();
DataCenterVO dataCenterVO = dataCenterDao.findById(zoneId);
VMTemplateVO template = templateDao.findById(templateId);
Hypervisor.HypervisorType type = template.getHypervisorType();
this.clusterTemplate = manager.getKubernetesServiceTemplate(dataCenterVO, type);
this.sshKeyFile = getManagementServerSshPublicKeyFile();
}
protected String readResourceFile(String resource) throws IOException {
return IOUtils.toString(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResourceAsStream(resource)), com.cloud.utils.StringUtils.getPreferredCharset());
}
protected String getControlNodeLoginUser() {
List<KubernetesClusterVmMapVO> vmMapVOList = getKubernetesClusterVMMaps();
if (vmMapVOList.size() > 0) {
long vmId = vmMapVOList.get(0).getVmId();
UserVmVO userVM = userVmDao.findById(vmId);
if (userVM == null) {
throw new CloudRuntimeException("Failed to find login user, Unable to log in to node to fetch details");
}
Set<String> vm = new HashSet<>();
vm.add(userVM.getName());
UserVmDetailVO vmDetail = userVmDetailsDao.findDetail(vmId, VmDetailConstants.CKS_CONTROL_NODE_LOGIN_USER);
if (vmDetail != null && !org.apache.commons.lang3.StringUtils.isEmpty(vmDetail.getValue())) {
return vmDetail.getValue();
} else {
return CLUSTER_NODE_VM_USER;
}
} else {
return CLUSTER_NODE_VM_USER;
}
}
protected void logMessage(final Level logLevel, final String message, final Exception e) {
if (logLevel == Level.INFO) {
if (LOGGER.isInfoEnabled()) {
if (e != null) {
LOGGER.info(message, e);
} else {
LOGGER.info(message);
}
}
} else if (logLevel == Level.DEBUG) {
if (LOGGER.isDebugEnabled()) {
if (e != null) {
LOGGER.debug(message, e);
} else {
LOGGER.debug(message);
}
}
} else if (logLevel == Level.WARN) {
if (e != null) {
LOGGER.warn(message, e);
} else {
LOGGER.warn(message);
}
} else {
if (e != null) {
LOGGER.error(message, e);
} else {
LOGGER.error(message);
}
}
}
protected void logTransitStateDetachIsoAndThrow(final Level logLevel, final String message, final KubernetesCluster kubernetesCluster,
final List<UserVm> clusterVMs, final KubernetesCluster.Event event, final Exception e) throws CloudRuntimeException {
logMessage(logLevel, message, e);
stateTransitTo(kubernetesCluster.getId(), event);
detachIsoKubernetesVMs(clusterVMs);
if (e == null) {
throw new CloudRuntimeException(message);
}
throw new CloudRuntimeException(message, e);
}
protected void deleteTemplateLaunchPermission() {
if (clusterTemplate != null && owner != null) {
LOGGER.info("Revoking launch permission for systemVM template");
launchPermissionDao.removePermissions(clusterTemplate.getId(), Collections.singletonList(owner.getId()));
}
}
protected void logTransitStateAndThrow(final Level logLevel, final String message, final Long kubernetesClusterId, final KubernetesCluster.Event event, final Exception e) throws CloudRuntimeException {
logMessage(logLevel, message, e);
if (kubernetesClusterId != null && event != null) {
stateTransitTo(kubernetesClusterId, event);
}
deleteTemplateLaunchPermission();
if (e == null) {
throw new CloudRuntimeException(message);
}
throw new CloudRuntimeException(message, e);
}
protected void logTransitStateAndThrow(final Level logLevel, final String message, final Long kubernetesClusterId, final KubernetesCluster.Event event) throws CloudRuntimeException {
logTransitStateAndThrow(logLevel, message, kubernetesClusterId, event, null);
}
protected void logAndThrow(final Level logLevel, final String message) throws CloudRuntimeException {
logTransitStateAndThrow(logLevel, message, null, null, null);
}
protected void logAndThrow(final Level logLevel, final String message, final Exception ex) throws CloudRuntimeException {
logTransitStateAndThrow(logLevel, message, null, null, ex);
}
protected File getManagementServerSshPublicKeyFile() {
boolean devel = Boolean.parseBoolean(configurationDao.getValue("developer"));
String keyFile = String.format("%s/.ssh/id_rsa", System.getProperty("user.home"));
if (devel) {
keyFile += ".cloud";
}
return new File(keyFile);
}
protected KubernetesClusterVmMapVO addKubernetesClusterVm(final long kubernetesClusterId, final long vmId, boolean isControlNode) {
return Transaction.execute(new TransactionCallback<KubernetesClusterVmMapVO>() {
@Override
public KubernetesClusterVmMapVO doInTransaction(TransactionStatus status) {
KubernetesClusterVmMapVO newClusterVmMap = new KubernetesClusterVmMapVO(kubernetesClusterId, vmId, isControlNode);
kubernetesClusterVmMapDao.persist(newClusterVmMap);
return newClusterVmMap;
}
});
}
private UserVm fetchControlVmIfMissing(final UserVm controlVm) {
if (controlVm != null) {
return controlVm;
}
List<KubernetesClusterVmMapVO> clusterVMs = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
if (CollectionUtils.isEmpty(clusterVMs)) {
LOGGER.warn(String.format("Unable to retrieve VMs for Kubernetes cluster : %s", kubernetesCluster.getName()));
return null;
}
List<Long> vmIds = new ArrayList<>();
for (KubernetesClusterVmMapVO vmMap : clusterVMs) {
vmIds.add(vmMap.getVmId());
}
Collections.sort(vmIds);
return userVmDao.findById(vmIds.get(0));
}
protected String getControlVmPrivateIp() {
String ip = null;
UserVm vm = fetchControlVmIfMissing(null);
if (vm != null) {
ip = vm.getPrivateIpAddress();
}
return ip;
}
protected IpAddress getNetworkSourceNatIp(Network network) {
List<? extends IpAddress> addresses = networkModel.listPublicIpsAssignedToGuestNtwk(network.getId(), true);
if (CollectionUtils.isNotEmpty(addresses)) {
return addresses.get(0);
}
LOGGER.warn(String.format("No public IP addresses found for network : %s, Kubernetes cluster : %s", network.getName(), kubernetesCluster.getName()));
return null;
}
protected IpAddress getVpcTierKubernetesPublicIp(Network network) {
KubernetesClusterDetailsVO detailsVO = kubernetesClusterDetailsDao.findDetail(kubernetesCluster.getId(), ApiConstants.PUBLIC_IP_ID);
if (detailsVO == null || StringUtils.isEmpty(detailsVO.getValue())) {
return null;
}
IpAddress address = ipAddressDao.findByUuid(detailsVO.getValue());
if (address == null || network.getVpcId() != address.getVpcId()) {
LOGGER.warn(String.format("Public IP with ID: %s linked to the Kubernetes cluster: %s is not usable", detailsVO.getValue(), kubernetesCluster.getName()));
return null;
}
return address;
}
protected IpAddress acquireVpcTierKubernetesPublicIp(Network network) throws
InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException {
IpAddress ip = networkService.allocateIP(owner, kubernetesCluster.getZoneId(), network.getId(), null, null);
if (ip == null) {
return null;
}
ip = vpcService.associateIPToVpc(ip.getId(), network.getVpcId());
ip = ipAddressManager.associateIPToGuestNetwork(ip.getId(), network.getId(), false);
kubernetesClusterDetailsDao.addDetail(kubernetesCluster.getId(), ApiConstants.PUBLIC_IP_ID, ip.getUuid(), false);
return ip;
}
protected Pair<String, Integer> getKubernetesClusterServerIpSshPortForIsolatedNetwork(Network network) {
String ip = null;
IpAddress address = getNetworkSourceNatIp(network);
if (address != null) {
ip = address.getAddress().addr();
}
return new Pair<>(ip, CLUSTER_NODES_DEFAULT_START_SSH_PORT);
}
protected Pair<String, Integer> getKubernetesClusterServerIpSshPortForSharedNetwork(UserVm controlVm) {
int port = DEFAULT_SSH_PORT;
controlVm = fetchControlVmIfMissing(controlVm);
if (controlVm == null) {
LOGGER.warn(String.format("Unable to retrieve control VM for Kubernetes cluster : %s", kubernetesCluster.getName()));
return new Pair<>(null, port);
}
return new Pair<>(controlVm.getPrivateIpAddress(), port);
}
protected Pair<String, Integer> getKubernetesClusterServerIpSshPortForVpcTier(Network network,
boolean acquireNewPublicIpForVpcTierIfNeeded) throws
InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException {
int port = CLUSTER_NODES_DEFAULT_START_SSH_PORT;
IpAddress address = getVpcTierKubernetesPublicIp(network);
if (address != null) {
return new Pair<>(address.getAddress().addr(), port);
}
if (acquireNewPublicIpForVpcTierIfNeeded) {
address = acquireVpcTierKubernetesPublicIp(network);
if (address != null) {
return new Pair<>(address.getAddress().addr(), port);
}
}
LOGGER.warn(String.format("No public IP found for the the VPC tier: %s, Kubernetes cluster : %s", network, kubernetesCluster.getName()));
return new Pair<>(null, port);
}
protected Pair<String, Integer> getKubernetesClusterServerIpSshPort(UserVm controlVm, boolean acquireNewPublicIpForVpcTierIfNeeded) throws
InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException {
int port = CLUSTER_NODES_DEFAULT_START_SSH_PORT;
KubernetesClusterDetailsVO detail = kubernetesClusterDetailsDao.findDetail(kubernetesCluster.getId(), ApiConstants.EXTERNAL_LOAD_BALANCER_IP_ADDRESS);
if (detail != null && StringUtils.isNotEmpty(detail.getValue())) {
return new Pair<>(detail.getValue(), port);
}
Network network = networkDao.findById(kubernetesCluster.getNetworkId());
if (network == null) {
LOGGER.warn(String.format("Network for Kubernetes cluster : %s cannot be found", kubernetesCluster.getName()));
return new Pair<>(null, port);
}
if (network.getVpcId() != null) {
return getKubernetesClusterServerIpSshPortForVpcTier(network, acquireNewPublicIpForVpcTierIfNeeded);
}
if (Network.GuestType.Isolated.equals(network.getGuestType())) {
return getKubernetesClusterServerIpSshPortForIsolatedNetwork(network);
} else if (Network.GuestType.Shared.equals(network.getGuestType())) {
return getKubernetesClusterServerIpSshPortForSharedNetwork(controlVm);
}
LOGGER.warn(String.format("Unable to retrieve server IP address for Kubernetes cluster : %s", kubernetesCluster.getName()));
return new Pair<>(null, port);
}
protected Pair<String, Integer> getKubernetesClusterServerIpSshPort(UserVm controlVm) {
try {
return getKubernetesClusterServerIpSshPort(controlVm, false);
} catch (InsufficientAddressCapacityException | ResourceAllocationException | ResourceUnavailableException e) {
LOGGER.debug("This exception should not have occurred", e);
}
return new Pair<>(null, CLUSTER_NODES_DEFAULT_START_SSH_PORT);
}
protected void attachIsoKubernetesVMs(List<UserVm> clusterVMs, final KubernetesSupportedVersion kubernetesSupportedVersion) throws CloudRuntimeException {
KubernetesSupportedVersion version = kubernetesSupportedVersion;
if (kubernetesSupportedVersion == null) {
version = kubernetesSupportedVersionDao.findById(kubernetesCluster.getKubernetesVersionId());
}
KubernetesCluster.Event failedEvent = KubernetesCluster.Event.OperationFailed;
KubernetesCluster cluster = kubernetesClusterDao.findById(kubernetesCluster.getId());
if (cluster != null && cluster.getState() == KubernetesCluster.State.Starting) {
failedEvent = KubernetesCluster.Event.CreateFailed;
}
if (version == null) {
logTransitStateAndThrow(Level.ERROR, String .format("Unable to find Kubernetes version for cluster : %s", kubernetesCluster.getName()), kubernetesCluster.getId(), failedEvent);
}
VMTemplateVO iso = templateDao.findById(version.getIsoId());
if (iso == null) {
logTransitStateAndThrow(Level.ERROR, String.format("Unable to attach ISO to Kubernetes cluster : %s. Binaries ISO not found.", kubernetesCluster.getName()), kubernetesCluster.getId(), failedEvent);
}
if (!iso.getFormat().equals(Storage.ImageFormat.ISO)) {
logTransitStateAndThrow(Level.ERROR, String.format("Unable to attach ISO to Kubernetes cluster : %s. Invalid Binaries ISO.", kubernetesCluster.getName()), kubernetesCluster.getId(), failedEvent);
}
if (!iso.getState().equals(VirtualMachineTemplate.State.Active)) {
logTransitStateAndThrow(Level.ERROR, String.format("Unable to attach ISO to Kubernetes cluster : %s. Binaries ISO not active.", kubernetesCluster.getName()), kubernetesCluster.getId(), failedEvent);
}
for (UserVm vm : clusterVMs) {
try {
templateService.attachIso(iso.getId(), vm.getId(), true);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(String.format("Attached binaries ISO for VM : %s in cluster: %s", vm.getDisplayName(), kubernetesCluster.getName()));
}
} catch (CloudRuntimeException ex) {
logTransitStateAndThrow(Level.ERROR, String.format("Failed to attach binaries ISO for VM : %s in the Kubernetes cluster name: %s", vm.getDisplayName(), kubernetesCluster.getName()), kubernetesCluster.getId(), failedEvent, ex);
}
}
}
protected void attachIsoKubernetesVMs(List<UserVm> clusterVMs) throws CloudRuntimeException {
attachIsoKubernetesVMs(clusterVMs, null);
}
protected void detachIsoKubernetesVMs(List<UserVm> clusterVMs) {
for (UserVm vm : clusterVMs) {
boolean result = false;
try {
result = templateService.detachIso(vm.getId(), true);
} catch (CloudRuntimeException ex) {
LOGGER.warn(String.format("Failed to detach binaries ISO from VM : %s in the Kubernetes cluster : %s ", vm.getDisplayName(), kubernetesCluster.getName()), ex);
}
if (result) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info(String.format("Detached Kubernetes binaries from VM : %s in the Kubernetes cluster : %s", vm.getDisplayName(), kubernetesCluster.getName()));
}
continue;
}
LOGGER.warn(String.format("Failed to detach binaries ISO from VM : %s in the Kubernetes cluster : %s ", vm.getDisplayName(), kubernetesCluster.getName()));
}
}
protected List<KubernetesClusterVmMapVO> getKubernetesClusterVMMaps() {
List<KubernetesClusterVmMapVO> clusterVMs = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId());
return clusterVMs;
}
protected List<KubernetesClusterVmMapVO> getKubernetesClusterVMMapsForNodes(List<Long> nodeIds) {
return kubernetesClusterVmMapDao.listByClusterIdAndVmIdsIn(kubernetesCluster.getId(), nodeIds);
}
protected List<UserVm> getKubernetesClusterVMs() {
List<UserVm> vmList = new ArrayList<>();
List<KubernetesClusterVmMapVO> clusterVMs = getKubernetesClusterVMMaps();
if (!CollectionUtils.isEmpty(clusterVMs)) {
for (KubernetesClusterVmMapVO vmMap : clusterVMs) {
vmList.add(userVmDao.findById(vmMap.getVmId()));
}
}
return vmList;
}
protected void updateLoginUserDetails(List<Long> clusterVMs) {
if (clusterVMs == null) {
clusterVMs = getKubernetesClusterVMMaps().stream().map(KubernetesClusterVmMapVO::getVmId).collect(Collectors.toList());
}
if (!CollectionUtils.isEmpty(clusterVMs)) {
for (Long vmId : clusterVMs) {
UserVm controlNode = userVmDao.findById(vmId);
if (controlNode != null) {
userVmDetailsDao.addDetail(vmId, VmDetailConstants.CKS_CONTROL_NODE_LOGIN_USER, CLUSTER_NODE_VM_USER, true);
}
}
}
}
protected boolean stateTransitTo(long kubernetesClusterId, KubernetesCluster.Event e) {
KubernetesClusterVO kubernetesCluster = kubernetesClusterDao.findById(kubernetesClusterId);
try {
return _stateMachine.transitTo(kubernetesCluster, e, null, kubernetesClusterDao);
} catch (NoTransitionException nte) {
LOGGER.warn(String.format("Failed to transition state of the Kubernetes cluster : %s in state %s on event %s",
kubernetesCluster.getName(), kubernetesCluster.getState().toString(), e.toString()), nte);
return false;
}
}
protected boolean createCloudStackSecret(String[] keys) {
File pkFile = getManagementServerSshPublicKeyFile();
Pair<String, Integer> publicIpSshPort = getKubernetesClusterServerIpSshPort(null);
publicIpAddress = publicIpSshPort.first();
sshPort = publicIpSshPort.second();
try {
String command = String.format("sudo %s/%s -u '%s' -k '%s' -s '%s'",
scriptPath, deploySecretsScriptFilename, ApiServiceConfiguration.ApiServletPath.value(), keys[0], keys[1]);
Account account = accountDao.findById(kubernetesCluster.getAccountId());
if (account != null && account.getType() == Account.Type.PROJECT) {
String projectId = projectService.findByProjectAccountId(account.getId()).getUuid();
command = String.format("%s -p '%s'", command, projectId);
}
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
pkFile, null, command, 10000, 10000, 60000);
return result.first();
} catch (Exception e) {
String msg = String.format("Failed to add cloudstack-secret to Kubernetes cluster: %s", kubernetesCluster.getName());
LOGGER.warn(msg, e);
}
return false;
}
protected File retrieveScriptFile(String filename) {
File file = null;
try {
String data = readResourceFile("/script/" + filename);
file = File.createTempFile(filename, ".sh");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write(data);
writer.close();
} catch (IOException e) {
logAndThrow(Level.ERROR, String.format("Kubernetes Cluster %s : Failed to to fetch script %s",
kubernetesCluster.getName(), filename), e);
}
return file;
}
protected void retrieveScriptFiles() {
deploySecretsScriptFile = retrieveScriptFile(deploySecretsScriptFilename);
deployProviderScriptFile = retrieveScriptFile(deployProviderScriptFilename);
autoscaleScriptFile = retrieveScriptFile(autoscaleScriptFilename);
}
protected void copyScripts(String nodeAddress, final int sshPort) {
copyScriptFile(nodeAddress, sshPort, deploySecretsScriptFile, deploySecretsScriptFilename);
copyScriptFile(nodeAddress, sshPort, deployProviderScriptFile, deployProviderScriptFilename);
copyScriptFile(nodeAddress, sshPort, autoscaleScriptFile, autoscaleScriptFilename);
}
protected void copyScriptFile(String nodeAddress, final int sshPort, File file, String desitnation) {
try {
SshHelper.scpTo(nodeAddress, sshPort, getControlNodeLoginUser(), sshKeyFile, null,
"~/", file.getAbsolutePath(), "0755");
String cmdStr = String.format("sudo mv ~/%s %s/%s", file.getName(), scriptPath, desitnation);
SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(), sshKeyFile, null,
cmdStr, 10000, 10000, 10 * 60 * 1000);
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
}
protected boolean taintControlNodes() {
StringBuilder commands = new StringBuilder();
List<KubernetesClusterVmMapVO> vmMapVOList = getKubernetesClusterVMMaps();
for(KubernetesClusterVmMapVO vmMap :vmMapVOList) {
if(!vmMap.isControlNode()) {
continue;
}
String name = userVmDao.findById(vmMap.getVmId()).getDisplayName().toLowerCase();
String command = String.format("sudo /opt/bin/kubectl annotate node %s cluster-autoscaler.kubernetes.io/scale-down-disabled=true ; ", name);
commands.append(command);
}
try {
File pkFile = getManagementServerSshPublicKeyFile();
Pair<String, Integer> publicIpSshPort = getKubernetesClusterServerIpSshPort(null);
publicIpAddress = publicIpSshPort.first();
sshPort = publicIpSshPort.second();
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
pkFile, null, commands.toString(), 10000, 10000, 60000);
return result.first();
} catch (Exception e) {
String msg = String.format("Failed to taint control nodes on : %s : %s", kubernetesCluster.getName(), e.getMessage());
logMessage(Level.ERROR, msg, e);
return false;
}
}
protected boolean deployProvider() {
Network network = networkDao.findById(kubernetesCluster.getNetworkId());
// Since the provider creates IP addresses, don't deploy it unless the underlying network supports it
if (network.getGuestType() != GuestType.Isolated) {
logMessage(Level.INFO, String.format("Skipping adding the provider as %s is not on an isolated network",
kubernetesCluster.getName()), null);
return true;
}
File pkFile = getManagementServerSshPublicKeyFile();
Pair<String, Integer> publicIpSshPort = getKubernetesClusterServerIpSshPort(null);
publicIpAddress = publicIpSshPort.first();
sshPort = publicIpSshPort.second();
try {
String command = String.format("sudo %s/%s", scriptPath, deployProviderScriptFilename);
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
pkFile, null, command, 10000, 10000, 60000);
// Maybe the file isn't present. Try and copy it
if (!result.first()) {
logMessage(Level.INFO, "Provider files missing. Adding them now", null);
retrieveScriptFiles();
copyScripts(publicIpAddress, sshPort);
if (!createCloudStackSecret(keys)) {
logTransitStateAndThrow(Level.ERROR, String.format("Failed to setup keys for Kubernetes cluster %s",
kubernetesCluster.getName()), kubernetesCluster.getId(), KubernetesCluster.Event.OperationFailed);
}
// If at first you don't succeed ...
result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
pkFile, null, command, 10000, 10000, 60000);
if (!result.first()) {
throw new CloudRuntimeException(result.second());
}
}
return true;
} catch (Exception e) {
String msg = String.format("Failed to deploy kubernetes provider: %s : %s", kubernetesCluster.getName(), e.getMessage());
logAndThrow(Level.ERROR, msg);
return false;
}
}
public void setKeys(String[] keys) {
this.keys = keys;
}
}