diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /config-model-api/src/main/java/com/yahoo |
Publish
Diffstat (limited to 'config-model-api/src/main/java/com/yahoo')
28 files changed, 1485 insertions, 0 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationFile.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationFile.java new file mode 100644 index 00000000000..585fb6902ab --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationFile.java @@ -0,0 +1,179 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import com.yahoo.path.Path; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +/** + * An application file represents a file within an application package. This class can be used to traverse the entire + * application package file structure, as well as read and write files to it, and create directories. + * + * @author lulf + * @since 5.1 + */ +public abstract class ApplicationFile implements Comparable<ApplicationFile> { + private static final String metaDir = ".meta"; + public static final String ContentStatusNew = "new"; + public static final String ContentStatusChanged = "changed"; + public static final String ContentStatusDeleted = "deleted"; + protected final Path path; + private static final PathFilter defaultFilter = path1 -> true; + + protected ApplicationFile(Path path) { + this.path = path; + } + + /** + * Check whether or not this file is a directory. + * + * @return true if it is, false if not. + */ + public abstract boolean isDirectory(); + + /** + * Test whether or not this file exists. + * + * @return true if it exists, false if not. + */ + public abstract boolean exists(); + + /** + * Create a {@link Reader} for the contents of this file. + * + * @return A {@link Reader} that should be closed after use. + * @throws FileNotFoundException if the file is not found. + */ + public abstract Reader createReader() throws FileNotFoundException; + + + /** + * Create an {@link InputStream} for the contents of this file. + * + * @return An {@link InputStream} that should be closed after use. + * @throws FileNotFoundException if the file is not found. + */ + public abstract InputStream createInputStream() throws FileNotFoundException; + + /** + * Create a directory at the path represented by this file. Parent directories will + * be automatically created. + * + * @return this + * @throws IllegalArgumentException if the directory already exists. + */ + public abstract ApplicationFile createDirectory(); + + /** + * Write the contents from this reader to this file. Any existing content will be overwritten! + * + * @param input A reader pointing to the content that should be written. + * @return this + */ + public abstract ApplicationFile writeFile(Reader input); + + /** + * List the files under this directory. If this is file, an empty list is returned. + * Only immediate files/subdirectories are returned. + * + * @return a list of files in this directory. + */ + public List<ApplicationFile> listFiles() { + return listFiles(defaultFilter); + } + + /** + * List the files under this directory. If this is file, an empty list is returned. + * Only immediate files/subdirectories are returned. + * + * @param filter A filter functor for filtering path names + * @return a list of files in this directory. + */ + public abstract List<ApplicationFile> listFiles(PathFilter filter); + + /** + * List the files in this directory, optionally list files for subdirectories recursively as well. + * + * @param recurse Set to true if all files in the directory tree should be returned. + * @return a list of files in this directory. + */ + public List<ApplicationFile> listFiles(boolean recurse) { + List<ApplicationFile> ret = new ArrayList<>(); + List<ApplicationFile> files = listFiles(); + ret.addAll(files); + if (recurse) { + for (ApplicationFile file : files) { + if (file.isDirectory()) { + ret.addAll(file.listFiles(recurse)); + } + } + } + return ret; + } + + /** + * Delete the file pointed to by this. If it is a non-empty directory, the operation will throw. + * + * @return this. + * @throws RuntimeException if the file is a directory and not empty. + */ + public abstract ApplicationFile delete(); + + /** + * Get the path that this file represents. + * + * @return a Path + */ + public Path getPath() { + return path; + } + + @Override + public String toString() { + return path.toString(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ApplicationFile) { + return path.equals(((ApplicationFile) other).path); + } + return false; + } + + protected Path getMetaPath() { + if (path.toString().equals("")) { + return Path.fromString(metaDir).append(".root"); + } else { + return path.getParentPath().append(metaDir).append(path.getName()); + } + } + + public abstract MetaData getMetaData(); + + public static class MetaData { + public String status = "unknown"; + public String md5 = ""; + + public MetaData() { } + + public MetaData(String status, String md5) { + this.status = status; + this.md5= md5; + } + + public String getStatus() { + return status; + } + + public String getMd5() { + return md5; + } + } + + public static interface PathFilter { + boolean accept(Path path); + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java new file mode 100644 index 00000000000..98d5fa92cac --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationMetaData.java @@ -0,0 +1,149 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import com.yahoo.slime.*; + +import com.yahoo.text.Utf8; + +import java.io.*; + +/** + * Metadata about an application package. + * + * @author musum + * @since 5.0 + */ +public class ApplicationMetaData { + private final String deployedByUser; + private final String deployedFromDir; + private final long deployTimestamp; + private final long generation; + private final long previousActiveGeneration; + private final String checkSum; + private final String appName; + + public ApplicationMetaData(File appDir, String deployedByUser, String deployedFromDir, Long deployTimestamp, + String checkSum, Long generation, long previousActiveGeneration) { + this(deployedByUser, deployedFromDir, deployTimestamp, appDir.getName(), checkSum, generation, previousActiveGeneration); + } + + public ApplicationMetaData(String deployedByUser, String deployedFromDir, Long deployTimestamp, String applicationName, String checkSum, Long generation, long previousActiveGeneration) { + this.appName = applicationName; + this.deployedByUser = deployedByUser; + this.deployedFromDir = deployedFromDir; + this.deployTimestamp = deployTimestamp; + this.checkSum = checkSum; + this.generation = generation; + this.previousActiveGeneration = previousActiveGeneration; + } + + /** + * Gets the name of the application (name of the directory from which application was deployed. + * Will return null if a problem occurred while getting metadata + * + * @return application name + */ + public String getApplicationName() { + return appName; + } + + /** + * Gets the user who deployed the application. + * Will return null if a problem occurred while getting metadata + * + * @return user name for the user who ran "deploy-application" + */ + public String getDeployedByUser() { + return deployedByUser; + } + + /** + * Gets the directory where the application was deployed from. + * Will return null if a problem occurred while getting metadata + * + * @return path to raw deploy directory (for the original application) + */ + public String getDeployPath() { + return deployedFromDir; + } + + /** + * Gets the time the application was deployed + * Will return null if a problem occurred while getting metadata + * + * @return timestamp for when "deploy-application" was run. In ms. + */ + public Long getDeployTimestamp() { + return deployTimestamp; + } + + /** + * Gets the time the application was deployed + * Will return null if a problem occurred while getting metadata + * + * @return timestamp for when "deploy-application" was run. In ms. + */ + public Long getGeneration() { + return generation; + } + + /** + * Returns an md5 hash of the contents of the application package + * @return an md5sum of the application package + */ + public String getCheckSum() { + return checkSum; + } + + /** + * Returns the previously active generation at the point when this application was created. + * @return a generation. + */ + public long getPreviousActiveGeneration() { + return previousActiveGeneration; + } + + @Override + public String toString() { + return deployedByUser + ", " + deployedFromDir + ", " + deployTimestamp + ", " + generation + ", " + checkSum + ", " + previousActiveGeneration; + } + + public static ApplicationMetaData fromJsonString(String jsonString) { + try { + Slime data = new Slime(); + new JsonDecoder().decode(data, Utf8.toBytes(jsonString)); + Inspector root = data.get(); + Inspector deploy = root.field("deploy"); + Inspector app = root.field("application"); + return new ApplicationMetaData(deploy.field("user").asString(), deploy.field("from").asString(), deploy.field("timestamp").asLong(), app.field("name").asString(), app.field("checksum").asString(), app.field("generation").asLong(), app.field("previousActiveGeneration").asLong()); + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing json metadata", e); + } + } + + public Slime getSlime() { + Slime slime = new Slime(); + Cursor meta = slime.setObject(); + Cursor deploy = meta.setObject("deploy"); + deploy.setString("user", deployedByUser); + deploy.setString("from", deployedFromDir); + deploy.setLong("timestamp", deployTimestamp); + Cursor app = meta.setObject("application"); + app.setString("name", appName); + app.setString("checksum", checkSum); + app.setLong("generation", generation); + app.setLong("previousActiveGeneration", previousActiveGeneration); + return slime; + } + + public String asJsonString() { + Slime slime = getSlime(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + new JsonFormat(false).encode(baos, slime); + return baos.toString("UTF-8"); + } catch (IOException e) { + throw new RuntimeException("Unable to encode metadata", e); + } + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java new file mode 100644 index 00000000000..96f58bf140a --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java @@ -0,0 +1,260 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import com.yahoo.config.provision.ProvisionInfo; +import com.yahoo.config.provision.Version; +import com.yahoo.config.provision.Zone; +import com.yahoo.path.Path; +import com.yahoo.io.IOUtils; +import com.yahoo.io.reader.NamedReader; +import com.yahoo.text.XML; +import com.yahoo.vespa.config.ConfigDefinitionKey; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.*; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Represents an application package, that is, used as input when creating a VespaModel and as + * a general reference to all contents in an application. + * + * The class hides detail as to whether the source is local files or ZooKeeper + * data in config server. + * + * Anyone wanting to access application data should use this interface. + * + * @author vegardh + */ +public interface ApplicationPackage { + + // Caution!! If you add something here it must probably also be added to ZooKeeperClient.feedZKAppPkg + + String HOSTS = "hosts.xml"; + String SERVICES = "services.xml"; + + Path SEARCH_DEFINITIONS_DIR = Path.fromString("searchdefinitions"); + String COMPONENT_DIR = "components"; + String TEMPLATES_DIR = "templates"; + String FILES_DIR = "files"; + String SEARCHCHAINS_DIR = "search/chains"; + String DOCPROCCHAINS_DIR = "docproc/chains"; + String PROCESSORCHAINS_DIR = "processor/chains"; + String ROUTINGTABLES_DIR = "routing/tables"; + + // NOTE: this directory is created in serverdb during deploy, and should not exist in the original user application + /** Do not use */ + String CONFIG_DEFINITIONS_DIR = "configdefinitions"; + + Path QUERY_PROFILES_DIR= Path.fromString("search/query-profiles"); + Path QUERY_PROFILE_TYPES_DIR= Path.fromString("search/query-profiles/types"); + Path PAGE_TEMPLATES_DIR= Path.fromString("page-templates"); + Path RULES_DIR = Path.fromString("rules"); + + Path DEPLOYMENT_FILE = Path.fromString("deployment.xml"); + Path VALIDATION_OVERRIDES = Path.fromString("validation-overrides.xml"); + + String SD_NAME_SUFFIX = ".sd"; + String RANKEXPRESSION_NAME_SUFFIX = ".expression"; + String RULES_NAME_SUFFIX = ".sr"; + String EXT_DIR = "ext"; + + String PERMANENT_SERVICES = "permanent-services.xml"; + + /** + * The name of the application package + * + * @return the name of the application (i.e the directory where the application package was deployed from) + */ + String getApplicationName(); + + /** + * Contents of services.xml. Caller must close reader after use. + * @return a Reader, or null if no services.xml/vespa-services.xml present + */ + Reader getServices(); + + /** + * Contents of hosts.xml. Caller must close reader after use. + * @return a Reader, or null if no hosts.xml/vespa-hosts.xml present + */ + Reader getHosts(); + + /** + * Returns the include dirs given by the user in the services.xml file. + */ + default List<String> getUserIncludeDirs() { + throw new UnsupportedOperationException( + "This application package does not have special handling for user include dirs."); + } + + default void validateIncludeDir(String dirName) { + throw new UnsupportedOperationException("" + + "This application package does not support validation of include dirs."); + } + + /** + * Readers for all the search definition files for this. + * @return a list of readers for search definitions + */ + Collection<NamedReader> searchDefinitionContents(); + + /** + * Subclass hook. + * Returns a mapping from def key to a file name that can be used for lookup. + * @return An mapping of all config definition combos available in this package. + */ + Map<ConfigDefinitionKey, UnparsedConfigDefinition> getAllExistingConfigDefs(); + + /** + * Returns the files in a directory as readers. The readers <b>must</b> + * be closed by the caller. + * + * + * @param pathFromRoot the relative path string from the root of the application package + * @param suffix the suffix of files to return, or null to return all + * @param recurse return files in all subdirectories (recursively) as well + * @return a list of the files at this location, or an empty list (never null) + * if the directory does not exist or is empty. The list gets owned by the caller + * and can be modified freely. + */ + List<NamedReader> getFiles(Path pathFromRoot, String suffix, boolean recurse); + + /** Same as getFiles(pathFromRoot,suffix,false) */ + default List<NamedReader> getFiles(Path pathFromRoot, String suffix) { + return getFiles(pathFromRoot,suffix,false); + } + + /** Returns the major version this application is valid for, or empty if it is valid for all versions */ + default Optional<Integer> getMajorVersion() { + Element servicesElement = XML.getDocument(getServices()).getDocumentElement(); + if (servicesElement == null) return Optional.empty(); + String majorVersionString = servicesElement.getAttribute("major-version"); + if (majorVersionString == null || majorVersionString.isEmpty()) + return Optional.empty(); + try { + return Optional.of(Integer.parseInt(majorVersionString)); + } + catch (NumberFormatException e) { + throw new IllegalArgumentException("major-version must be an integer number, not '" + majorVersionString + "'"); + } + } + + /** + * Gets a file from the root of the application package + * + * + * @param relativePath The relative path of the file within this application package. + * @return reader for file + * @throws IllegalArgumentException if the given path does not exist + */ + ApplicationFile getFile(Path relativePath); + + /** Does {@link #getFiles} on the query profile directory and gets all xml files */ + default List<NamedReader> getQueryProfileFiles() { return getFiles(QUERY_PROFILES_DIR,".xml"); } + + /** Does {@link #getFiles} on the query profile directory and gets all xml files */ + default List<NamedReader> getQueryProfileTypeFiles() { return getFiles(QUERY_PROFILE_TYPES_DIR,".xml"); } + + /** Does {@link #getFiles} on the page template directory and gets all xml files */ + default List<NamedReader> getPageTemplateFiles() { return getFiles(PAGE_TEMPLATES_DIR,".xml"); } + + //For generating error messages + String getHostSource(); + String getServicesSource(); + + Optional<Reader> getDeployment(); + Optional<Reader> getValidationOverrides(); + + List<ComponentInfo> getComponentsInfo(Version vespaVersion); + + /** + * Reads a ranking expression from file to a string and returns it. + * + * @param name the name of the file to return, relative to the search definition directory in the application package + * @return the content of a ranking expression file + * @throws IllegalArgumentException if the file was not found or could not be read + */ + Reader getRankingExpression(String name); + + /** + * Returns the name-payload pairs of any sd files under path/searchdefinitions/ in the given jar bundle + * @param bundle The jar file, which will be closed afterwards by this method. + * @param path For example 'complex/' + * @return map of the SD payloads + * @throws IOException if it is reading sd files fails + */ + static Map<String, String> getBundleSdFiles(String path, JarFile bundle) throws IOException { + Map<String,String> ret = new LinkedHashMap<>(); + for (Enumeration<JarEntry> e = bundle.entries(); e.hasMoreElements();) { + JarEntry je=e.nextElement(); + if (je.getName().startsWith(path+SEARCH_DEFINITIONS_DIR+"/") && je.getName().endsWith(SD_NAME_SUFFIX)) { + String contents = IOUtils.readAll(new InputStreamReader(bundle.getInputStream(je))); + ret.put(getFileName(je), contents); + } + } + bundle.close(); + return ret; + } + + /** + * The name of an SD in a JarEntry + */ + static String getFileName(JarEntry je) { + String name = je.getName(); + name = name.replaceAll(".*/", ""); + return name; + } + + /** + * Gets the ApplicationMetaData instance for this application package. + * @return an ApplicationMetaData instance + */ + default ApplicationMetaData getMetaData() { return null; } + + default File getFileReference(Path pathRelativeToAppDir) { + throw new UnsupportedOperationException("This application package cannot return file references"); + } + + default void validateXML(DeployLogger logger) throws IOException { + throw new UnsupportedOperationException("This application package cannot validate XML"); + } + + default void validateXML(DeployLogger logger, Optional<Version> vespaVersion) throws IOException { + throw new UnsupportedOperationException("This application package cannot validate XML"); + } + + default void writeMetaData() throws IOException { + throw new UnsupportedOperationException("This application package cannot write its metadata"); + } + + default Map<Version, ProvisionInfo> getProvisionInfoMap() { + return Collections.emptyMap(); + } + + default Map<Version, FileRegistry> getFileRegistryMap() { + return Collections.emptyMap(); + } + + Collection<NamedReader> getSearchDefinitions(); + + /** + * Preprocess an application for a given zone and return a new application package pointing to the preprocessed + * application package. This is the entry point for the multi environment application package support. This method + * will not mutate the existing application package. + * + * @param zone A valid {@link Zone} instance, used to decide which parts of services to keep and remove + * @param ruleConfigDeriver ignored + * @param logger A {@link DeployLogger} to add output that will be returned to the user + * + * @return A new application package instance pointing to a new location + */ + default ApplicationPackage preprocess(Zone zone, RuleConfigDeriver ruleConfigDeriver, DeployLogger logger) throws IOException, TransformerException, ParserConfigurationException, SAXException { + throw new UnsupportedOperationException("This application package does not support preprocessing"); + } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java new file mode 100644 index 00000000000..42732ddfc47 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ComponentInfo.java @@ -0,0 +1,21 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + + +/** + * Describes a component residing in the components directory. + * <p>TODO: add support for component versions.</p> + * @author tonytv + */ +public class ComponentInfo { + final String pathRelativeToAppDir; + + public ComponentInfo(String pathRelativeToAppDir) { + this.pathRelativeToAppDir = pathRelativeToAppDir; + } + + //get path relative to app dir + public String getPathRelativeToAppDir() { + return pathRelativeToAppDir; + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeployLogger.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeployLogger.java new file mode 100644 index 00000000000..3ba95fb6b2a --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeployLogger.java @@ -0,0 +1,16 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import java.util.logging.Level; + +/** + * Used during application deployment to persist and propagate messages to end user + * + * @author lulf + * @since 5.1 + */ +public interface DeployLogger { + + void log(Level level, String message); + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java new file mode 100644 index 00000000000..e758e332901 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java @@ -0,0 +1,182 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.RegionName; +import com.yahoo.text.XML; +import org.w3c.dom.Element; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Specifies the environments and regions to which an application should be deployed. + * This may be used both for inspection as part of an application model and to answer + * queries about deployment from the command line. A main method is included for the latter usage. + * + * @author bratseth + */ +public class DeploymentSpec { + + private final Optional<String> globalServiceId; + private final List<DeclaredZone> zones; + + public DeploymentSpec(List<DeclaredZone> zones, Optional<String> globalServiceId) { + this.zones = Collections.unmodifiableList(new ArrayList<>(zones)); + this.globalServiceId = globalServiceId; + } + + /** Returns the zones this declares as a read-only list. */ + public List<DeclaredZone> zones() { return zones; } + + /** Returns whether this deployment spec specifies the given zone, either implicitly or explicitly */ + public boolean includes(Environment environment, Optional<RegionName> region) { + for (DeclaredZone declaredZone : zones) + if (declaredZone.matches(environment, region)) return true; + return false; + } + + /** Returns the ID of the service to expose through global routing, if present */ + public Optional<String> globalServiceId() { + return globalServiceId; + } + + /** + * Creates a deployment spec from XML. + * + * @throws IllegalArgumentException if the XML is invalid + */ + public static DeploymentSpec fromXml(Reader reader) { + List<DeclaredZone> zones = new ArrayList<>(); + Element root = XML.getDocument(reader).getDocumentElement(); + Optional<String> globalServiceId = Optional.empty(); + for (Element environmentTag : XML.getChildren(root)) { + Environment environment = Environment.from(environmentTag.getTagName()); + List<Element> regionTags = XML.getChildren(environmentTag, "region"); + if (regionTags.isEmpty()) { + zones.add(new DeclaredZone(environment, Optional.empty(), false)); + } + else { + for (Element regionTag : regionTags) { + RegionName region = RegionName.from(XML.getValue(regionTag).trim()); + boolean active = environment == Environment.prod && readActive(regionTag); + zones.add(new DeclaredZone(environment, Optional.of(region), active)); + } + } + + if (Environment.prod.equals(environment)) { + globalServiceId = readGlobalServiceId(environmentTag); + } else if (readGlobalServiceId(environmentTag).isPresent()) { + throw new IllegalArgumentException("Attribute 'global-service-id' is only valid on 'prod' tag."); + } + } + return new DeploymentSpec(zones, globalServiceId); + } + + private static Optional<String> readGlobalServiceId(Element environmentTag) { + String globalServiceId = environmentTag.getAttribute("global-service-id"); + if (globalServiceId == null || globalServiceId.isEmpty()) { + return Optional.empty(); + } + else { + return Optional.of(globalServiceId); + } + } + + private static boolean readActive(Element regionTag) { + String activeValue = regionTag.getAttribute("active"); + if ("true".equals(activeValue)) return true; + if ("false".equals(activeValue)) return false; + throw new IllegalArgumentException("Region tags must have an 'active' attribute set to 'true' or 'false' " + + "to control whether the region should receive production traffic"); + } + + public static String toMessageString(Throwable t) { + StringBuilder b = new StringBuilder(); + String lastMessage = null; + String message; + for (; t != null; t = t.getCause()) { + message = t.getMessage(); + if (message == null) continue; + if (message.equals(lastMessage)) continue; + if (b.length() > 0) { + b.append(": "); + } + b.append(message); + lastMessage = message; + } + return b.toString(); + } + + /** This may be invoked by a continuous build */ + public static void main (String[] args) { + if (args.length != 2 && args.length != 3) { + System.err.println("Usage: DeploymentSpec [file] [environment] [region]?" + + "Returns 0 if the specified zone matches the deployment spec, 1 otherwise"); + System.exit(1); + } + + try (BufferedReader reader = new BufferedReader(new FileReader(args[0]))) { + DeploymentSpec spec = DeploymentSpec.fromXml(reader); + Environment environment = Environment.from(args[1]); + Optional<RegionName> region = args.length == 3 ? Optional.of(RegionName.from(args[2])) : Optional.empty(); + if (spec.includes(environment, region)) + System.exit(0); + else + System.exit(1); + } + catch (Exception e) { + System.err.println("Exception checking deployment spec: " + toMessageString(e)); + System.exit(1); + } + } + + public static class DeclaredZone { + + private final Environment environment; + + private Optional<RegionName> region; + + private final boolean active; + + public DeclaredZone(Environment environment, Optional<RegionName> region, boolean active) { + this.environment = environment; + this.region = region; + this.active = active; + } + + public Environment environment() { return environment; } + + /** The region name, or empty if not declared */ + public Optional<RegionName> region() { return region; } + + /** Returns whether this zone should receive production traffic */ + public boolean active() { return active; } + + public boolean matches(Environment environment, Optional<RegionName> region) { + if (environment.equals(this.environment) && region.equals(this.region)) return true; + if ( ! region.isPresent() && prerequisite(environment)) return true; + return false; + } + + /** + * Returns whether deployment in the given environment is a prerequisite of deployment in this environment + * + * The required progression leading to prerequisites is test, staging, prod. + */ + private boolean prerequisite(Environment environment) { + if (this.environment == Environment.prod) + return environment == Environment.staging || environment == Environment.test; + if (this.environment == Environment.staging) + return environment == Environment.test; + return false; + } + + } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java b/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java new file mode 100644 index 00000000000..fe4aab72cb0 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/FileRegistry.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import java.util.List; +import java.util.Set; + +import com.yahoo.config.FileReference; + +/** + * @author tonytv + */ +public interface FileRegistry { + + FileReference addFile(String relativePath); + + /** + * Returns the name of the host which is the source of the files + */ + String fileSourceHost(); + + Set<String> allRelativePaths(); + + List<Entry> export(); + + class Entry { + public final String relativePath; + public final FileReference reference; + + public Entry(String relativePath, FileReference reference) { + this.relativePath = relativePath; + this.reference = reference; + } + } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/RuleConfigDeriver.java b/config-model-api/src/main/java/com/yahoo/config/application/api/RuleConfigDeriver.java new file mode 100644 index 00000000000..770d8c450ee --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/RuleConfigDeriver.java @@ -0,0 +1,14 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +/** + * Interface to hide dependency on prelude from application package module due to semantic rules + * rewriting. + * + * @author lulf + * @since 5.22 + */ +// TODO: This is not used any more. Do a phased removal while keeping config model compatibility +public interface RuleConfigDeriver { + void derive(String ruleBaseDir, String outputDir) throws Exception; +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java b/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java new file mode 100644 index 00000000000..5954047c564 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/UnparsedConfigDefinition.java @@ -0,0 +1,14 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.application.api; + +import com.yahoo.vespa.config.ConfigDefinition; + +/** + * Represents a config definition that has not been parsed. + * @author lulf + * @since 5.20 +*/ +public interface UnparsedConfigDefinition { + public ConfigDefinition parse(); + public String getUnparsedContent(); +} diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/package-info.java b/config-model-api/src/main/java/com/yahoo/config/application/api/package-info.java new file mode 100644 index 00000000000..ed696f98d2a --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/package-info.java @@ -0,0 +1,7 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +@PublicApi +package com.yahoo.config.application.api; + +import com.yahoo.api.annotations.PublicApi; +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java new file mode 100644 index 00000000000..30e2df9acf9 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeAction.java @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import java.util.List; + +/** + * Contains the action to be performed on the given services to handle a config change + * between the current active model and the next model to prepare. + * + * @author geirst + * @since 5.40 + */ +public interface ConfigChangeAction { + + enum Type { + RESTART("restart"), REFEED("refeed"); + + private final String type; + + Type(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + } + + /** Returns what type of action is required to handle this config change */ + Type getType(); + + /** Returns a message describing the config change in detail */ + String getMessage(); + + /** Returns the list of services where the action must be performed */ + List<ServiceInfo> getServices(); + + /** Returns whether this change should be allowed */ + boolean allowed(); + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java new file mode 100644 index 00000000000..7f70733fb8c --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRefeedAction.java @@ -0,0 +1,22 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +/** + * Represents an action to re-feed a document type in order to handle a config change. + * + * @author geirst + * @since 5.43 + */ +public interface ConfigChangeRefeedAction extends ConfigChangeAction { + + @Override + default Type getType() { return Type.REFEED; } + + /** Returns the name identifying this kind of change, used to identify names which should be allowed */ + // Remove this default implementation when model versions earlier than 5.125 are gone + default String name() { return ""; } + + /** Returns the name of the document type that one must re-feed to handle this config change */ + String getDocumentType(); + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java new file mode 100644 index 00000000000..3add3ef1519 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigChangeRestartAction.java @@ -0,0 +1,19 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +/** + * Represents an action to restart services in order to handle a config change. + * + * @author geirst + * @since 5.43 + */ +public interface ConfigChangeRestartAction extends ConfigChangeAction { + + @Override + default Type getType() { return Type.RESTART; } + + /** Restarts are handled automatically so they are allowed */ + @Override + default boolean allowed() { return true; } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java new file mode 100644 index 00000000000..69dc3f08d64 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionRepo.java @@ -0,0 +1,23 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.vespa.config.ConfigDefinitionKey; +import com.yahoo.vespa.config.buildergen.ConfigDefinition; + +import java.util.Map; + +/** + * Represents a repository of config definitions. + * + * @author lulf + * @since 5.10 + */ +public interface ConfigDefinitionRepo { + + /** + * Retrieve a map with all configdefinitions in this repo. + */ + Map<ConfigDefinitionKey, ConfigDefinition> getConfigDefinitions(); + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java new file mode 100644 index 00000000000..ca2064ed363 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigDefinitionStore.java @@ -0,0 +1,13 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.vespa.config.ConfigDefinition; +import com.yahoo.vespa.config.ConfigDefinitionKey; + +/** + * @author lulf + * @since 5.1 + */ +public interface ConfigDefinitionStore { + ConfigDefinition getConfigDefinition(ConfigDefinitionKey defKey); +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java new file mode 100644 index 00000000000..3cebcb6e71b --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigModelPlugin.java @@ -0,0 +1,10 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +/** + * Interface of config model plugins. + * + * @author lulf + */ +public interface ConfigModelPlugin { +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java new file mode 100644 index 00000000000..0a43f190675 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ConfigServerSpec.java @@ -0,0 +1,14 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +/** + * Provides information about a configserver instance. + * + * @author tonytv + */ +public interface ConfigServerSpec { + public String getHostName(); + public int getConfigServerPort(); + public int getHttpPort(); + public int getZooKeeperPort(); +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java b/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java new file mode 100644 index 00000000000..604a9edf003 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/FileDistribution.java @@ -0,0 +1,28 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.config.FileReference; +import com.yahoo.vespa.defaults.Defaults; + +import java.io.File; +import java.util.Collection; +import java.util.Set; + +/** + * Interface for models towards filedistribution. + * + * @author lulf + * @since 5.1 + */ +public interface FileDistribution { + + void sendDeployedFiles(String hostName, Set<FileReference> fileReferences); + void reloadDeployFileDistributor(); + void limitSendingOfDeployedFilesTo(Collection<String> hostNames); + void removeDeploymentsThatHaveDifferentApplicationId(Collection<String> targetHostnames); + + static File getDefaultFileDBPath() { + return new File(Defaults.getDefaults().vespaHome() + "var/db/vespa/filedistribution"); + } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java new file mode 100644 index 00000000000..dbb2a1a07a9 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/HostInfo.java @@ -0,0 +1,48 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import java.util.Collection; + +/* + * Contains information about a host and what services are running on it. + * + * @author lulf + * @since 5.37 + */ +public class HostInfo { + private final String hostname; + private final Collection<ServiceInfo> services; + + public HostInfo(String hostName, Collection<ServiceInfo> services) { + this.hostname = hostName; + this.services = services; + } + + public String getHostname() { + return hostname; + } + + public Collection<ServiceInfo> getServices() { + return services; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HostInfo hostInfo = (HostInfo) o; + + if (!hostname.equals(hostInfo.hostname)) return false; + if (services != null ? !services.equals(hostInfo.services) : hostInfo.services != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = hostname.hashCode(); + result = 31 * result + (services != null ? services.hashCode() : 0); + return result; + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java b/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java new file mode 100644 index 00000000000..3e14485dd5a --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/HostProvisioner.java @@ -0,0 +1,33 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.config.provision.*; + +import java.util.List; +import java.util.logging.Level; + +/** + * Interface towards the host provisioner used to build a {@link Model}. The difference between this provisioner + * and {@link com.yahoo.config.provision.Provisioner}, is that this interface only exposes methods needed + * to build the model. + * + * @author lulf + */ +public interface HostProvisioner { + + /** Allocates a single host for a service */ + // TODO: Remove + HostSpec allocateHost(String alias); + + /** + * Prepares allocation of a set of hosts with a given type, common id and the amount. + * + * @param cluster the cluster to allocate nodes to + * @param capacity the capacity describing the capacity requested + * @param groups the number of groups to divide the nodes into + * @param logger a logger to which messages to the deployer may be written + * @return the specification of the allocated hosts + */ + List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger); + +}
\ No newline at end of file diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java b/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java new file mode 100644 index 00000000000..998ca6cb53c --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java @@ -0,0 +1,99 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.config.codegen.InnerCNode; +import com.yahoo.config.provision.ProvisionInfo; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.config.ConfigPayload; +import com.yahoo.vespa.config.buildergen.ConfigDefinition; + +import java.io.IOException; +import java.util.Optional; +import java.util.Set; +import java.util.Collection; + +/** + * A {@link Model} represents the interface towards the model of an entire tenant, and defines methods + * for querying this model. + * + * @author lulf + * @since 5.1 + */ +public interface Model { + + /** + * Resolves a config using a given def file, apply overrides and returns it. + * + * @param configKey The key of the config to retrieve. + * @param targetDef The config definition to use for applying defaults. + * @return override The global override to apply to the generated config. + */ + ConfigPayload getConfig(ConfigKey<?> configKey, ConfigDefinition targetDef, ConfigPayload override) throws IOException; + + /** + * TODO: Remove this method once no fat bundles implementing it anymore. + * Use {@link Model#getConfig(ConfigKey, ConfigDefinition, ConfigPayload)} instead. + * + * Resolves a config using a given def file, apply overrides and returns it. + * + * @param configKey The key of the config to retrieve. + * @param targetDef The config definition to use for applying defaults. + * @return override The global override to apply to the generated config. + */ + ConfigPayload getConfig(ConfigKey<?> configKey, InnerCNode targetDef, ConfigPayload override) throws IOException; + + /** + * Produces a set of the valid config keys for this model. + */ + Set<ConfigKey<?>> allConfigsProduced(); + + /** + * Returns information about all hosts used in this model. + */ + Collection<HostInfo> getHosts(); + + /** + * Returns all the config ids available for this model. + */ + Set<String> allConfigIds(); + + /** + * Asks the {@link Model} instance to distribute files using provided filedistribution instance. + * @param fileDistribution {@link com.yahoo.config.model.api.FileDistribution} instance that can be called to distribute files. + */ + void distributeFiles(FileDistribution fileDistribution); + + + /** + * Tells file distributor to rescan all files. At the moment this is a very expensive operation, so should only be done + * once per deployment. + * @param fileDistribution {@link com.yahoo.config.model.api.FileDistribution} instance. + */ + default void reloadDeployFileDistributor(FileDistribution fileDistribution) { } + + /** + * Get the provisioning info for this model. + * @return {@link ProvisionInfo} instance, if available. + */ + Optional<ProvisionInfo> getProvisionInfo(); + + /** + * Returns whether this application allows serving config request for a different version. + * This is a validation override which is useful when we skip loading old config models + * due to some problem, or when we need to try a newer version of the platform on some node. + */ + default boolean allowModelVersionMismatch() { return false; } + + /** + * Returns whether old config models should be loaded (default) or not. + * Skipping old config models is a validation override which is useful when the old model + * version is known to contain some incompatibility with the application package + * and it is believed that the latest model version will deliver usable config for old versions + * requesting config. + * <p> + * If a model returns true to this it should also return true to {@link #allowModelVersionMismatch} + * or clients requesting config for those old models will not get config at all. + */ + default boolean skipOldConfigModels() { return false; } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java new file mode 100644 index 00000000000..9226e3bbe11 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -0,0 +1,43 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.application.api.DeployLogger; +import com.yahoo.config.application.api.FileRegistry; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.Rotation; +import com.yahoo.config.provision.Version; +import com.yahoo.config.provision.Zone; + +import java.io.File; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * Model context containing state provided to model factories. + * + * @author lulf + */ +public interface ModelContext { + + ApplicationPackage applicationPackage(); + Optional<Model> previousModel(); + Optional<ApplicationPackage> permanentApplicationPackage(); + Optional<HostProvisioner> hostProvisioner(); + DeployLogger deployLogger(); + ConfigDefinitionRepo configDefinitionRepo(); + FileRegistry getFileRegistry(); + Properties properties(); + default Optional<File> appDir() { return Optional.empty();} + default Optional<Version> vespaVersion() { return Optional.empty(); } + + interface Properties { + boolean multitenant(); + ApplicationId applicationId(); + List<ConfigServerSpec> configServerSpecs(); + boolean hostedVespa(); + Zone zone(); + Set<Rotation> rotations(); + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java new file mode 100644 index 00000000000..10738653129 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelCreateResult.java @@ -0,0 +1,28 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import java.util.List; + +/** + * The result after creating and validating a Model. + * + * @author geirst + * @since 5.39 + */ +public class ModelCreateResult { + + private final Model model; + private final List<ConfigChangeAction> configChangeActions; + + public ModelCreateResult(Model model, List<ConfigChangeAction> configChangeActions) { + this.model = model; + this.configChangeActions = configChangeActions; + } + + /** The model these changes apply to */ + public Model getModel() { return model; } + + /** Returns the actions that needs to be done to successfully start using the new model */ + public List<ConfigChangeAction> getConfigChangeActions() { return configChangeActions; } + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java new file mode 100644 index 00000000000..a6f7b8096ea --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelFactory.java @@ -0,0 +1,39 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import com.yahoo.config.provision.Version; + +/** + * Factory for config models. + */ +public interface ModelFactory { + + /** + * Gets version of this {@link ModelFactory}. The version will be used to dispatch deployments to the correct + * {@link ModelFactory}. + * + * @return The version of a {@link Model} instance that this factory can create. + */ + Version getVersion(); + + /** + * Creates an instance of a {@link Model}. The resulting instance will be used to serve config. No model + * validation will be done, calling this method presupposes that {@link #createAndValidateModel} has already + * been called. + * + * @param modelContext An instance of {@link ModelContext}, containing dependencies for creating a {@link Model}. + * @return a {@link Model} instance. + */ + Model createModel(ModelContext modelContext); + + /** + * Creates an instance of a {@link Model}. The resulting instance will be used to serve config. Any validation + * of a {@link Model} and the {@link ModelContext} can be done in this method. + * + * @param modelContext An instance of {@link ModelContext}, containing dependencies for creating a {@link Model}. + * @param ignoreValidationErrors true if validation errors should not trigger exceptions. + * @return a {@link ModelCreateResult} instance. + */ + ModelCreateResult createAndValidateModel(ModelContext modelContext, boolean ignoreValidationErrors); + +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java new file mode 100644 index 00000000000..236f951e4d3 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelState.java @@ -0,0 +1,9 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +/** + * @author lulf + */ +public interface ModelState { + Model getModel(); +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/PortInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/PortInfo.java new file mode 100644 index 00000000000..99f6ae469f3 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/PortInfo.java @@ -0,0 +1,46 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import java.util.Collection; + +/** + * Contains information about a port (port number and a collection of tags). + * + */ +public class PortInfo { + private final int port; + private final Collection<String> tags; + + public PortInfo(int port, Collection<String> tags) { + this.port = port; + this.tags = tags; + } + + public int getPort() { + return port; + } + + public Collection<String> getTags() { + return tags; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PortInfo portInfo = (PortInfo) o; + + if (port != portInfo.port) return false; + if (tags != null ? !tags.equals(portInfo.tags) : portInfo.tags != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = port; + result = 31 * result + (tags != null ? tags.hashCode() : 0); + return result; + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java new file mode 100644 index 00000000000..6d72da7df6f --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ServiceInfo.java @@ -0,0 +1,87 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.api; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Collection; + +/** + * Contains information about a service. + * + * @author lulf + * @since 5.37 + */ +public class ServiceInfo { + private final String serviceName; + private final String serviceType; + private final Collection<PortInfo> ports; + private final Map<String, String> properties; + private final String configId; + private final String hostName; + + public ServiceInfo(String serviceName, String serviceType, Collection<PortInfo> ports, Map<String, String> properties, + String configId, String hostName) { + + Objects.requireNonNull(configId); + + this.serviceName = serviceName; + this.serviceType = serviceType; + this.ports = ports; + this.properties = properties; + this.configId = configId; + this.hostName = hostName; + } + + public String getServiceName() { + return serviceName; + } + + public String getConfigId() { + return configId; + } + + public String getServiceType() { + return serviceType; + } + + public Optional<String> getProperty(String propertyName) { + return Optional.ofNullable(properties.get(propertyName)); + } + + public Collection<PortInfo> getPorts() { + return ports; + } + + public String getHostName() { + return hostName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ServiceInfo that = (ServiceInfo) o; + + if (ports != null ? !ports.equals(that.ports) : that.ports != null) return false; + if (properties != null ? !properties.equals(that.properties) : that.properties != null) return false; + if (!serviceName.equals(that.serviceName)) return false; + if (!serviceType.equals(that.serviceType)) return false; + if (!configId.equals(that.configId)) return false; + if (!hostName.equals(that.hostName)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = serviceName.hashCode(); + result = 31 * result + serviceType.hashCode(); + result = 31 * result + (ports != null ? ports.hashCode() : 0); + result = 31 * result + (properties != null ? properties.hashCode() : 0); + result = 31 * result + configId.hashCode(); + result = 31 * result + hostName.hashCode(); + return result; + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/package-info.java b/config-model-api/src/main/java/com/yahoo/config/model/api/package-info.java new file mode 100644 index 00000000000..730d23f5086 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.config.model.api; + +import com.yahoo.osgi.annotation.ExportPackage; |