|
| 1 | +// Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +// or more contributor license agreements. See the NOTICE file |
| 3 | +// distributed with this work for additional information |
| 4 | +// regarding copyright ownership. The ASF licenses this file |
| 5 | +// to you under the Apache License, Version 2.0 (the |
| 6 | +// "License"); you may not use this file except in compliance |
| 7 | +// with the License. You may obtain a copy of the License at |
| 8 | +// |
| 9 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +// |
| 11 | +// Unless required by applicable law or agreed to in writing, |
| 12 | +// software distributed under the License is distributed on an |
| 13 | +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +// KIND, either express or implied. See the License for the |
| 15 | +// specific language governing permissions and limitations |
| 16 | +// under the License. |
| 17 | + |
| 18 | +package org.apache.cloudstack.framework.extensions.manager; |
| 19 | + |
| 20 | +import java.io.File; |
| 21 | +import java.io.IOException; |
| 22 | +import java.nio.file.Files; |
| 23 | +import java.nio.file.Path; |
| 24 | +import java.nio.file.Paths; |
| 25 | +import java.util.Collection; |
| 26 | +import java.util.Collections; |
| 27 | +import java.util.HashMap; |
| 28 | +import java.util.List; |
| 29 | +import java.util.Map; |
| 30 | +import java.util.UUID; |
| 31 | + |
| 32 | +import javax.inject.Inject; |
| 33 | + |
| 34 | +import org.apache.cloudstack.extension.Extension; |
| 35 | +import org.apache.cloudstack.framework.extensions.api.ImportExtensionCmd; |
| 36 | +import org.apache.cloudstack.framework.extensions.dao.ExtensionDao; |
| 37 | +import org.apache.cloudstack.framework.extensions.util.ExtensionConfig; |
| 38 | +import org.apache.cloudstack.framework.extensions.util.YamlParser; |
| 39 | +import org.apache.cloudstack.framework.extensions.util.ZipExtractor; |
| 40 | +import org.apache.cloudstack.framework.extensions.vo.ExtensionVO; |
| 41 | +import org.apache.commons.lang3.StringUtils; |
| 42 | + |
| 43 | +import com.cloud.hypervisor.ExternalProvisioner; |
| 44 | +import com.cloud.utils.HttpUtils; |
| 45 | +import com.cloud.utils.component.ManagerBase; |
| 46 | +import com.cloud.utils.db.Transaction; |
| 47 | +import com.cloud.utils.db.TransactionCallbackWithException; |
| 48 | +import com.cloud.utils.exception.CloudRuntimeException; |
| 49 | + |
| 50 | +public class ExtensionsImportManagerImpl extends ManagerBase implements ExtensionsImportManager { |
| 51 | + |
| 52 | + @Inject |
| 53 | + ExtensionsManager extensionsManager; |
| 54 | + |
| 55 | + @Inject |
| 56 | + ExternalProvisioner externalProvisioner; |
| 57 | + |
| 58 | + @Inject |
| 59 | + ExtensionDao extensionDao; |
| 60 | + |
| 61 | + protected Extension importExtensionInternal(String manifestUrl, Path tempDir) { |
| 62 | + Path manifestPath = tempDir.resolve("manifest.yaml"); |
| 63 | + HttpUtils.downloadFileWithProgress(manifestUrl, manifestPath.toString(), logger); |
| 64 | + if (!Files.exists(manifestPath)) { |
| 65 | + throw new CloudRuntimeException("Failed to download extension manifest from URL: " + manifestUrl); |
| 66 | + } |
| 67 | + final ExtensionConfig extensionConfig = YamlParser.parseYamlFile(manifestPath.toString()); |
| 68 | + //Parse the manifest and create the extension |
| 69 | + final String name = extensionConfig.metadata.name; |
| 70 | + final String extensionArchiveURL = extensionConfig.getArchiveUrl(); |
| 71 | + ExtensionVO extensionByName = extensionDao.findByName(name); |
| 72 | + if (extensionByName != null) { |
| 73 | + throw new CloudRuntimeException("Extension by name already exists"); |
| 74 | + } |
| 75 | + if (StringUtils.isBlank(extensionArchiveURL)) { |
| 76 | + throw new CloudRuntimeException("Unable to retrieve archive URL for extension source during import"); |
| 77 | + } |
| 78 | + Path extensionArchivePath = tempDir.resolve(UUID.randomUUID() + ".zip"); |
| 79 | + HttpUtils.downloadFileWithProgress(extensionArchiveURL, extensionArchivePath.toString(), logger); |
| 80 | + if (!Files.exists(extensionArchivePath)) { |
| 81 | + throw new CloudRuntimeException("Failed to download extension archive from URL: " + extensionArchiveURL); |
| 82 | + } |
| 83 | + final String extensionRootPath = externalProvisioner.getExtensionsPath() + File.separator + name; |
| 84 | + try { |
| 85 | + ZipExtractor.extractZipContents(extensionArchivePath.toString(), extensionRootPath); |
| 86 | + } catch (IOException e) { |
| 87 | + throw new CloudRuntimeException("Failed to extract extension archive during import at: " + extensionRootPath, e); |
| 88 | + } |
| 89 | + return Transaction.execute((TransactionCallbackWithException<Extension, CloudRuntimeException>) status -> { |
| 90 | + Extension extension = extensionsManager.createExtension(name, extensionConfig.metadata.description, |
| 91 | + extensionConfig.spec.type, extensionConfig.spec.entrypoint.path, Extension.State.Enabled.name(), |
| 92 | + false, Collections.emptyMap()); |
| 93 | + |
| 94 | + for (ExtensionConfig.CustomAction action : extensionConfig.spec.customActions) { |
| 95 | + List<Map<String, String>> parameters = action.getParametersMapList(); |
| 96 | + Map<Integer, Collection<Map<String, String>>> parametersMap = new HashMap<>(); |
| 97 | + parametersMap.put(1, parameters); |
| 98 | + extensionsManager.addCustomAction(action.name, action.description, extension.getId(), |
| 99 | + action.resourcetype, action.allowedroletypes, action.timeout, true, parametersMap, |
| 100 | + null, null, Collections.emptyMap()); |
| 101 | + } |
| 102 | + return null; |
| 103 | + }); |
| 104 | + } |
| 105 | + |
| 106 | + @Override |
| 107 | + public Extension importExtension(ImportExtensionCmd cmd) { |
| 108 | + final String manifestUrl = cmd.getManifestUrl(); |
| 109 | + final String extensionsRootPath = externalProvisioner.getExtensionsPath(); |
| 110 | + |
| 111 | + Path tempDir; |
| 112 | + try { |
| 113 | + Path extensionsRootDir = Paths.get(extensionsRootPath); |
| 114 | + Files.createDirectories(extensionsRootDir); |
| 115 | + tempDir = Files.createTempDirectory(extensionsRootDir, "import-ext-"); |
| 116 | + |
| 117 | + } catch (IOException e) { |
| 118 | + logger.error("Failed to create working directory for import extension, {}", extensionsRootPath, e); |
| 119 | + throw new CloudRuntimeException("Failed to create working directory for import extension", e); |
| 120 | + } |
| 121 | + try { |
| 122 | + return importExtensionInternal(manifestUrl, tempDir); |
| 123 | + } catch (Exception e) { |
| 124 | + logger.error(e.getMessage(), e); |
| 125 | + throw e; |
| 126 | + }/* finally { |
| 127 | + FileUtil.deletePath(tempDir.toString()); |
| 128 | + }*/ |
| 129 | + } |
| 130 | +} |
0 commit comments