All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sitewhere.microservice.scripting.KubernetesScriptManagement Maven / Gradle / Ivy

The newest version!
/**
 * Copyright © 2014-2021 The SiteWhere Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sitewhere.microservice.scripting;

import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

import com.sitewhere.microservice.lifecycle.LifecycleComponent;
import com.sitewhere.spi.SiteWhereException;
import com.sitewhere.spi.SiteWhereSystemException;
import com.sitewhere.spi.error.ErrorCode;
import com.sitewhere.spi.error.ErrorLevel;
import com.sitewhere.spi.microservice.IFunctionIdentifier;
import com.sitewhere.spi.microservice.scripting.IScriptCreateRequest;
import com.sitewhere.spi.microservice.scripting.IScriptManagement;
import com.sitewhere.spi.microservice.scripting.IScriptMetadata;
import com.sitewhere.spi.microservice.scripting.IScriptVersion;

import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.sitewhere.k8s.crd.ResourceLabels;
import io.sitewhere.k8s.crd.tenant.scripting.SiteWhereScript;
import io.sitewhere.k8s.crd.tenant.scripting.SiteWhereScriptList;
import io.sitewhere.k8s.crd.tenant.scripting.SiteWhereScriptSpec;
import io.sitewhere.k8s.crd.tenant.scripting.version.SiteWhereScriptVersion;
import io.sitewhere.k8s.crd.tenant.scripting.version.SiteWhereScriptVersionList;
import io.sitewhere.k8s.crd.tenant.scripting.version.SiteWhereScriptVersionSpec;

/**
 * Default {@link IScriptManagement} implementation. Stores scripts in
 * Kubernetes.
 */
public class KubernetesScriptManagement extends LifecycleComponent implements IScriptManagement {

    /** Parser for ISO dates */
    private static DateTimeFormatter FORMATTER = ISODateTimeFormat.dateTimeParser();

    /*
     * @see com.sitewhere.spi.microservice.scripting.IScriptManagement#
     * getScriptMetadataList(com.sitewhere.spi.microservice.IFunctionIdentifier,
     * java.lang.String)
     */
    @Override
    public List getScriptMetadataList(IFunctionIdentifier identifier, String tenantId)
	    throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	Map labels = new HashMap<>();
	labels.put(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA, identifier.getPath());
	labels.put(ResourceLabels.LABEL_SITEWHERE_TENANT, tenantId);
	SiteWhereScriptList list = getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace)
		.withLabels(labels).list();

	// Convert scripts to SiteWhere API format.
	List results = new ArrayList<>();
	for (SiteWhereScript script : list.getItems()) {
	    results.add(convertScriptMetadata(script, false));
	}
	return results;
    }

    /*
     * @see com.sitewhere.spi.microservice.scripting.IScriptManagement#
     * getScriptMetadataListForCategory(com.sitewhere.spi.microservice.
     * IFunctionIdentifier, java.lang.String, java.lang.String)
     */
    @Override
    public List getScriptMetadataListForCategory(IFunctionIdentifier identifier, String tenantId,
	    String category) throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	Map labels = new HashMap<>();
	labels.put(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA, identifier.getPath());
	labels.put(ResourceLabels.LABEL_SITEWHERE_TENANT, tenantId);
	labels.put(ResourceLabels.LABEL_SCRIPTING_SCRIPT_CATEGORY, category);
	SiteWhereScriptList list = getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace)
		.withLabels(labels).list();

	// Convert scripts to SiteWhere API format.
	List results = new ArrayList<>();
	for (SiteWhereScript script : list.getItems()) {
	    results.add(convertScriptMetadata(script, false));
	}
	return results;
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#getScriptMetadata(
     * com.sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * java.lang.String)
     */
    @Override
    public IScriptMetadata getScriptMetadata(IFunctionIdentifier identifier, String tenantId, String scriptId)
	    throws SiteWhereException {
	SiteWhereScript match = getK8sScript(identifier, tenantId, scriptId);
	return convertScriptMetadata(match, true);
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#createScript(com.
     * sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * com.sitewhere.spi.microservice.scripting.IScriptCreateRequest)
     */
    @Override
    public IScriptMetadata createScript(IFunctionIdentifier identifier, String tenantId, IScriptCreateRequest request)
	    throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	SiteWhereScript existing = getK8sScript(identifier, tenantId, request.getId());
	if (existing != null) {
	    throw new SiteWhereException("A script with that id already exists.");
	}

	// Create script k8s object and populate metadata.
	SiteWhereScript k8sScript = createK8sScript(identifier, tenantId, request);
	k8sScript = getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace)
		.create(k8sScript);

	SiteWhereScriptVersion k8sVersion = createK8sScriptVersion(k8sScript, request, "Initial version.");
	k8sVersion = getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions().inNamespace(namespace)
		.create(k8sVersion);

	ScriptMetadata created = convertScriptMetadata(k8sScript, false);
	IScriptVersion version = convertScriptVersion(k8sVersion);

	activateScript(identifier, tenantId, request.getId(), version.getVersionId());
	return created;
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#getScriptContent(
     * com.sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * java.lang.String, java.lang.String)
     */
    @Override
    public byte[] getScriptContent(IFunctionIdentifier identifier, String tenantId, String scriptId, String versionId)
	    throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	SiteWhereScriptVersion version = getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions()
		.inNamespace(namespace).withName(versionId).get();
	return version != null ? version.getSpec().getContent().getBytes() : null;
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#updateScript(com.
     * sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * java.lang.String, java.lang.String,
     * com.sitewhere.spi.microservice.scripting.IScriptCreateRequest)
     */
    @Override
    public IScriptMetadata updateScript(IFunctionIdentifier identifier, String tenantId, String scriptId,
	    String versionId, IScriptCreateRequest request) throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	SiteWhereScript match = getK8sScript(identifier, tenantId, scriptId);
	if (match == null) {
	    throw new SiteWhereSystemException(ErrorCode.Error, ErrorLevel.ERROR);
	}

	if (request.getName() != null) {
	    match.getSpec().setName(request.getName());
	}
	if (request.getDescription() != null) {
	    match.getSpec().setDescription(request.getDescription());
	}
	getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace).createOrReplace(match);

	SiteWhereScriptVersion version = getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions()
		.inNamespace(namespace).withName(versionId).get();
	if (version != null) {
	    version.getSpec().setContent(new String(Base64.getDecoder().decode(request.getContent())));
	    getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions().inNamespace(namespace)
		    .createOrReplace(version);
	}

	return convertScriptMetadata(match, false);
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#cloneScript(com.
     * sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public IScriptVersion cloneScript(IFunctionIdentifier identifier, String tenantId, String scriptId,
	    String versionId, String comment) throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	SiteWhereScript match = getK8sScript(identifier, tenantId, scriptId);
	if (match == null) {
	    throw new SiteWhereSystemException(ErrorCode.Error, ErrorLevel.ERROR);
	}

	SiteWhereScriptVersion version = getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions()
		.inNamespace(namespace).withName(versionId).get();
	if (version == null) {
	    throw new SiteWhereSystemException(ErrorCode.Error, ErrorLevel.ERROR);
	}

	ScriptCreateRequest request = new ScriptCreateRequest();
	request.setContent(Base64.getEncoder().encodeToString(version.getSpec().getContent().getBytes()));
	SiteWhereScriptVersion k8sVersion = createK8sScriptVersion(match, request, comment);
	k8sVersion = getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions().inNamespace(namespace)
		.create(k8sVersion);
	return convertScriptVersion(k8sVersion);
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#activateScript(com
     * .sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * java.lang.String, java.lang.String)
     */
    @Override
    public IScriptMetadata activateScript(IFunctionIdentifier identifier, String tenantId, String scriptId,
	    String versionId) throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	SiteWhereScript match = getK8sScript(identifier, tenantId, scriptId);
	if (match == null) {
	    throw new SiteWhereSystemException(ErrorCode.Error, ErrorLevel.ERROR);
	}

	SiteWhereScriptVersion version = getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions()
		.inNamespace(namespace).withName(versionId).get();
	if (version == null) {
	    throw new SiteWhereSystemException(ErrorCode.Error, ErrorLevel.ERROR);
	}

	match.getSpec().setActiveVersion(versionId);
	match = getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace)
		.createOrReplace(match);
	return convertScriptMetadata(match, false);
    }

    /*
     * @see
     * com.sitewhere.spi.microservice.scripting.IScriptManagement#deleteScript(com.
     * sitewhere.spi.microservice.IFunctionIdentifier, java.lang.String,
     * java.lang.String)
     */
    @Override
    public IScriptMetadata deleteScript(IFunctionIdentifier identifier, String tenantId, String scriptId)
	    throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	SiteWhereScript match = getK8sScript(identifier, tenantId, scriptId);
	if (match == null) {
	    throw new SiteWhereSystemException(ErrorCode.Error, ErrorLevel.ERROR);
	}
	getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace)
		.withName(match.getMetadata().getName()).delete();
	return convertScriptMetadata(match, false);
    }

    /**
     * Get k8s script based on criteria.
     * 
     * @param identifier
     * @param tenantId
     * @param scriptId
     * @return
     * @throws SiteWhereException
     */
    protected SiteWhereScript getK8sScript(IFunctionIdentifier identifier, String tenantId, String scriptId)
	    throws SiteWhereException {
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	Map labels = new HashMap<>();
	labels.put(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA, identifier.getPath());
	labels.put(ResourceLabels.LABEL_SITEWHERE_TENANT, tenantId);
	labels.put(ResourceLabels.LABEL_SCRIPTING_SCRIPT_ID, scriptId);
	SiteWhereScriptList list = getMicroservice().getSiteWhereKubernetesClient().getScripts().inNamespace(namespace)
		.withLabels(labels).list();
	if (list.getItems().size() > 1) {
	    throw new SiteWhereException("Multiple scripts found matching criteria.");
	}
	if (list.getItems().size() == 0) {
	    return null;
	}
	return list.getItems().get(0);
    }

    /**
     * Create k8s script from request information.
     * 
     * @param identifier
     * @param tenantId
     * @param request
     * @return
     */
    protected SiteWhereScript createK8sScript(IFunctionIdentifier identifier, String tenantId,
	    IScriptCreateRequest request) {
	SiteWhereScript script = new SiteWhereScript();

	ObjectMeta meta = new ObjectMeta();
	String name = String.format("%s-%s-%s", identifier.getPath(), request.getId(),
		String.valueOf(System.currentTimeMillis()));
	meta.setName(name);
	meta.setNamespace(getMicroservice().getInstanceSettings().getKubernetesNamespace());
	Map labels = new HashMap<>();
	labels.put(ResourceLabels.LABEL_SITEWHERE_TENANT, tenantId);
	labels.put(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA, identifier.getPath());
	labels.put(ResourceLabels.LABEL_SCRIPTING_SCRIPT_ID, request.getId());
	labels.put(ResourceLabels.LABEL_SCRIPTING_SCRIPT_CATEGORY, request.getCategory());
	meta.setLabels(labels);
	script.setMetadata(meta);

	SiteWhereScriptSpec spec = new SiteWhereScriptSpec();
	spec.setScriptId(request.getId());
	spec.setName(request.getName());
	spec.setDescription(request.getDescription());
	spec.setInterpreterType(request.getInterpreterType());
	script.setSpec(spec);

	return script;
    }

    /**
     * Create k8s version from request information.
     * 
     * @param script
     * @param request
     * @param comment
     * @return
     */
    protected SiteWhereScriptVersion createK8sScriptVersion(SiteWhereScript script, IScriptCreateRequest request,
	    String comment) {
	Map scriptLabels = script.getMetadata().getLabels();
	SiteWhereScriptVersion version = new SiteWhereScriptVersion();

	ObjectMeta meta = new ObjectMeta();
	String name = String.format("%s-v%s", script.getMetadata().getName(),
		String.valueOf(System.currentTimeMillis()));
	meta.setName(name);
	meta.setNamespace(getMicroservice().getInstanceSettings().getKubernetesNamespace());
	Map labels = new HashMap<>();
	labels.put(ResourceLabels.LABEL_SITEWHERE_TENANT, scriptLabels.get(ResourceLabels.LABEL_SITEWHERE_TENANT));
	labels.put(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA,
		scriptLabels.get(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA));
	labels.put(ResourceLabels.LABEL_SCRIPTING_SCRIPT_ID,
		scriptLabels.get(ResourceLabels.LABEL_SCRIPTING_SCRIPT_ID));
	meta.setLabels(labels);
	version.setMetadata(meta);

	SiteWhereScriptVersionSpec spec = new SiteWhereScriptVersionSpec();
	spec.setComment(comment);
	spec.setContent(new String(Base64.getDecoder().decode(request.getContent())));
	version.setSpec(spec);

	return version;
    }

    /**
     * Create {@link ScriptMetadata} based on k8s {@link SiteWhereScript}.
     * 
     * @param script
     * @param includeVersions
     * @return
     */
    protected ScriptMetadata convertScriptMetadata(SiteWhereScript script, boolean includeVersions) {
	ScriptMetadata meta = new ScriptMetadata();
	meta.setId(script.getSpec().getScriptId());
	meta.setName(script.getSpec().getName());
	meta.setDescription(script.getSpec().getDescription());
	meta.setInterpreterType(script.getSpec().getInterpreterType());
	meta.setActiveVersion(script.getSpec().getActiveVersion());
	meta.setCategory(script.getMetadata().getLabels().get(ResourceLabels.LABEL_SCRIPTING_SCRIPT_CATEGORY));

	// TODO: This is expensive since it's a k8s API query for each script.
	if (includeVersions) {
	    meta.setVersions(new ArrayList());
	    SiteWhereScriptVersionList k8sVersions = getVersionsForScript(script);
	    for (SiteWhereScriptVersion k8sVersion : k8sVersions.getItems()) {
		meta.getVersions().add(convertScriptVersion(k8sVersion));
	    }
	    Collections.sort(meta.getVersions(), new Comparator() {

		@Override
		public int compare(IScriptVersion s1, IScriptVersion s2) {
		    return -1 * s1.getCreatedDate().compareTo(s2.getCreatedDate());
		}
	    });
	}

	return meta;
    }

    /**
     * Create {@link ScriptVersion} based on k8s {@link SiteWhereScriptVersion}.
     * 
     * @param k8s
     * @return
     */
    protected ScriptVersion convertScriptVersion(SiteWhereScriptVersion k8s) {
	ScriptVersion version = new ScriptVersion();
	version.setVersionId(k8s.getMetadata().getName());
	version.setCreatedDate(FORMATTER.parseDateTime(k8s.getMetadata().getCreationTimestamp()).toDate());
	version.setComment(k8s.getSpec().getComment());
	return version;
    }

    /**
     * Get versions for a given script.
     * 
     * @param script
     * @return
     */
    protected SiteWhereScriptVersionList getVersionsForScript(SiteWhereScript script) {
	String tenantId = script.getMetadata().getLabels().get(ResourceLabels.LABEL_SITEWHERE_TENANT);
	String functionalArea = script.getMetadata().getLabels().get(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA);
	String scriptId = script.getSpec().getScriptId();
	String namespace = getMicroservice().getInstanceSettings().getKubernetesNamespace();
	Map labels = new HashMap<>();
	labels.put(ResourceLabels.LABEL_SCRIPTING_SCRIPT_ID, scriptId);
	labels.put(ResourceLabels.LABEL_SITEWHERE_FUNCTIONAL_AREA, functionalArea);
	labels.put(ResourceLabels.LABEL_SITEWHERE_TENANT, tenantId);
	return getMicroservice().getSiteWhereKubernetesClient().getScriptsVersions().inNamespace(namespace)
		.withLabels(labels).list();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy