Skip to content

Commit

Permalink
feat: Support customizing image URL of built-in plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
CH3CHO committed Feb 26, 2025
1 parent 84a08e2 commit 6a7c271
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ public ResponseEntity<Response<WasmPlugin>> update(@PathVariable("name") @NotBla
throw new ValidationException("Plugin name in the URL doesn't match the one in the body.");
}
plugin.validate();
WasmPlugin updatedPlugin = Boolean.TRUE.equals(plugin.getBuiltIn())
? wasmPluginService.updateBuiltIn(plugin.getName(), plugin.getImageVersion())
WasmPlugin updatedPlugin = Boolean.TRUE.equals(plugin.getBuiltIn()) ? wasmPluginService.updateBuiltIn(plugin)
: wasmPluginService.updateCustom(plugin);
return ControllerUtil.buildResponseEntity(updatedPlugin);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,5 @@ public void validate() {
if (StringUtils.isBlank(imageRepository)) {
throw new ValidationException("imageRepository cannot be blank.");
}

if (StringUtils.isBlank(imageVersion)) {
throw new ValidationException("imageVersion cannot be blank.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public interface WasmPluginService {

String queryReadme(String name, String language);

WasmPlugin updateBuiltIn(String name, String imageVersion);
WasmPlugin updateBuiltIn(WasmPlugin plugin);

WasmPlugin addCustom(WasmPlugin plugin);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.higress.sdk.constant.Separators;
import com.alibaba.higress.sdk.exception.BusinessException;
import com.alibaba.higress.sdk.exception.NotFoundException;
import com.alibaba.higress.sdk.exception.ResourceConflictException;
Expand All @@ -61,7 +60,7 @@
import com.alibaba.higress.sdk.service.kubernetes.crd.wasm.V1alpha1WasmPlugin;
import com.alibaba.higress.sdk.service.kubernetes.crd.wasm.V1alpha1WasmPluginSpec;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Preconditions;
import com.google.common.annotations.VisibleForTesting;

import io.kubernetes.client.openapi.ApiException;
import io.swagger.v3.core.util.Json;
Expand Down Expand Up @@ -98,6 +97,12 @@ class WasmPluginServiceImpl implements WasmPluginService {
private static final String OPEN_API_V3_SCHEMA_PROPERTY_KEY = "openAPIV3Schema:";
private static final String YAML_EXAMPLE_PROPERTY_KEY = "example:";

private static final String NAME_PLACEHOLDER = "${name}";
private static final String VERSION_PLACEHOLDER = "${version}";

private static final String CUSTOM_IMAGE_URL_PATTERN_ENV = "HIGRESS_ADMIN_WASM_PLUGIN_CUSTOM_IMAGE_URL_PATTERN";
private static final String CUSTOM_IMAGE_URL_PATTERN_PROPERTY = "higress-admin.wasmplugin.custom-image-url-pattern";

private volatile List<PluginCacheItem> builtInPlugins = Collections.emptyList();

private final KubernetesClientService kubernetesClientService;
Expand All @@ -121,14 +126,16 @@ public void initialize() {

final List<PluginCacheItem> plugins = new ArrayList<>(properties.size());

final String customImageUrlPattern = loadCustomImageUrlPattern();

for (Object key : properties.keySet()) {
String pluginName = (String)key;
String imageRepository = properties.getProperty(pluginName);
if (StringUtils.isEmpty(imageRepository)) {
String imageUrl = properties.getProperty(pluginName);
if (StringUtils.isEmpty(imageUrl)) {
continue;
}

PluginCacheItem cacheItem = new PluginCacheItem(pluginName, imageRepository);
PluginCacheItem cacheItem = new PluginCacheItem(pluginName);

final String pluginFolder = PLUGINS_RESOURCE_FOLDER + pluginName + "/";
try (InputStream stream = getResourceAsStream(pluginFolder + SPEC_FILE)) {
Expand All @@ -145,6 +152,9 @@ public void initialize() {
ex);
}

cacheItem.imageUrl = StringUtils.isBlank(customImageUrlPattern) ? imageUrl
: formatImageUrl(customImageUrlPattern, cacheItem.plugin.getInfo());

cacheItem.setDefaultReadme(loadPluginReadme(pluginName, README_FILE));
cacheItem.setReadme(Language.ZH_CN.getCode(), loadPluginReadme(pluginName, README_CN_FILE));
cacheItem.setReadme(Language.EN_US.getCode(), loadPluginReadme(pluginName, README_EN_FILE));
Expand All @@ -165,6 +175,24 @@ public void initialize() {
this.builtInPlugins = plugins;
}

@VisibleForTesting
static String loadCustomImageUrlPattern() {
String value = System.getProperty(CUSTOM_IMAGE_URL_PATTERN_PROPERTY);
if (StringUtils.isEmpty(value)) {
value = System.getenv(CUSTOM_IMAGE_URL_PATTERN_ENV);
}
return value;
}

@VisibleForTesting
static String formatImageUrl(String pattern, PluginInfo pluginInfo) {
if (StringUtils.isEmpty(pattern)) {
return pattern;
}
return pattern.replace(NAME_PLACEHOLDER, pluginInfo.getName()).replace(VERSION_PLACEHOLDER,
pluginInfo.getVersion());
}

private void fillPluginConfigExample(Plugin plugin, String content) {
String example;
try {
Expand Down Expand Up @@ -386,19 +414,18 @@ public String queryReadme(String name, String language) {
}

@Override
public WasmPlugin updateBuiltIn(String name, String imageVersion) {
Preconditions.checkArgument(StringUtils.isNotEmpty(name), "name cannot be blank.");
Preconditions.checkArgument(StringUtils.isNotEmpty(imageVersion), "imageVersion cannot be blank.");
public WasmPlugin updateBuiltIn(WasmPlugin plugin) {
final String name = plugin.getName();

PluginCacheItem builtInPlugin =
PluginCacheItem cachedBuiltInPlugin =
builtInPlugins.stream().filter(p -> p.getName().equals(name)).findFirst().orElse(null);
if (builtInPlugin == null) {
if (cachedBuiltInPlugin == null) {
throw new ResourceConflictException("No built-in plugin is found with the given name: " + name);
}

List<V1alpha1WasmPlugin> existedCrs;
try {
final String pluginVersion = builtInPlugin.getPlugin().getInfo().getVersion();
final String pluginVersion = cachedBuiltInPlugin.getPlugin().getInfo().getVersion();
existedCrs = kubernetesClientService.listWasmPlugin(name, pluginVersion, true);
} catch (ApiException e) {
throw new BusinessException("Error occurs when checking existed Wasm plugins with name " + name, e);
Expand All @@ -407,9 +434,10 @@ public WasmPlugin updateBuiltIn(String name, String imageVersion) {
V1alpha1WasmPlugin updatedCr = null;

if (existedCrs.stream().allMatch(KubernetesUtil::isInternalResource)) {
WasmPlugin plugin = builtInPlugin.buildWasmPlugin();
plugin.setImageVersion(imageVersion);
V1alpha1WasmPlugin cr = kubernetesModelConverter.wasmPluginToCr(plugin);
WasmPlugin builtInPlugin = cachedBuiltInPlugin.buildWasmPlugin();
builtInPlugin.setImageRepository(plugin.getImageRepository());
builtInPlugin.setImageVersion(plugin.getImageVersion());
V1alpha1WasmPlugin cr = kubernetesModelConverter.wasmPluginToCr(builtInPlugin);
// Make sure it is disabled by default.
cr.getSpec().setDefaultConfigDisable(true);
try {
Expand All @@ -428,14 +456,8 @@ public WasmPlugin updateBuiltIn(String name, String imageVersion) {
if (spec == null) {
continue;
}
String url = spec.getUrl();
if (StringUtils.isEmpty(url)) {
continue;
}
ImageUrl imageUrl = ImageUrl.parse(url);
imageUrl.setTag(imageVersion);
ImageUrl imageUrl = new ImageUrl(plugin.getImageRepository(), plugin.getImageVersion());
spec.setUrl(imageUrl.toUrlString());

try {
updatedCr = kubernetesClientService.replaceWasmPlugin(existedCr);
} catch (ApiException e) {
Expand Down Expand Up @@ -604,17 +626,16 @@ private static class PluginCacheItem {
private static final String DEFAULT_README_KEY = "_default_";

private final String name;
private final String imageUrl;
private String imageUrl;
private Plugin plugin;
private String iconData;

@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private final Map<String, String> readmes = new HashMap<>(4);

public PluginCacheItem(String name, String imageUrl) {
public PluginCacheItem(String name) {
this.name = name;
this.imageUrl = imageUrl;
}

public String getDefaultReadme() {
Expand Down Expand Up @@ -642,13 +663,9 @@ public WasmPlugin buildWasmPlugin() {
public WasmPlugin buildWasmPlugin(String language) {
WasmPlugin wasmPlugin = new WasmPlugin();
wasmPlugin.setName(name);
int colonIndex = imageUrl.lastIndexOf(Separators.COLON);
if (colonIndex != -1) {
wasmPlugin.setImageRepository(imageUrl.substring(0, colonIndex));
wasmPlugin.setImageVersion(imageUrl.substring(colonIndex + 1));
} else {
wasmPlugin.setImageRepository(imageUrl);
}
ImageUrl imageUrlObj = ImageUrl.parse(imageUrl);
wasmPlugin.setImageRepository(imageUrlObj.getRepository());
wasmPlugin.setImageVersion(imageUrlObj.getTag());
wasmPlugin.setBuiltIn(true);

PluginInfo info = plugin.getInfo();
Expand All @@ -670,7 +687,8 @@ public WasmPlugin buildWasmPlugin(String language) {

PluginSpec spec = plugin.getSpec();
if (spec != null) {
wasmPlugin.setPhase("default".equals(spec.getPhase()) ? PluginPhase.UNSPECIFIED.getName() : spec.getPhase());
wasmPlugin
.setPhase("default".equals(spec.getPhase()) ? PluginPhase.UNSPECIFIED.getName() : spec.getPhase());
wasmPlugin.setPriority(spec.getPriority());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
*/
package com.alibaba.higress.sdk.service.kubernetes;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.higress.sdk.constant.CommonKey;
import com.alibaba.higress.sdk.constant.Separators;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
Expand All @@ -29,7 +32,7 @@ public class ImageUrl {
private String tag;

public String toUrlString() {
return tag != null ? repository + ":" + tag : repository;
return StringUtils.isNotBlank(tag) ? repository + ":" + tag : repository;
}

public static ImageUrl parse(String url) {
Expand All @@ -38,6 +41,10 @@ public static ImageUrl parse(String url) {
return new ImageUrl(url, null);
}
int protocolIndex = url.indexOf(CommonKey.PROTOCOL_KEYWORD);
if (protocolIndex != -1 && !url.startsWith(CommonKey.OCI_PROTOCOL)) {
// Not an OCI image URL, maybe an http:// or file:// URL
return new ImageUrl(url, null);
}
if (colonIndex <= protocolIndex) {
return new ImageUrl(url, null);
}
Expand Down
Loading

0 comments on commit 6a7c271

Please sign in to comment.