From 3eefb81679eb3b3da6b782583b060166b39befd7 Mon Sep 17 00:00:00 2001 From: Olli Virtanen Date: Thu, 31 May 2018 10:58:25 +0200 Subject: Scala code in standalone-container converted to Java --- .../container/impl/ClassLoaderOsgiFramework.java | 569 +++++++++++++++++++++ .../container/impl/StandaloneContainerRunner.java | 34 ++ .../yahoo/container/standalone/LocalFileDb.java | 97 ++++ .../standalone/StandaloneContainerApplication.java | 304 +++++++++++ .../standalone/StandaloneSubscriberFactory.java | 127 +++++ .../container/impl/ClassLoaderOsgiFramework.scala | 206 -------- .../container/impl/StandaloneContainerRunner.scala | 27 - .../com/yahoo/container/standalone/Converter.scala | 26 - .../yahoo/container/standalone/Environment.scala | 23 - .../yahoo/container/standalone/LocalFileDb.scala | 75 --- .../StandaloneContainerApplication.scala | 231 --------- .../standalone/StandaloneSubscriberFactory.scala | 78 --- .../CloudConfigInstallVariablesTest.java | 67 +++ .../container/standalone/StandaloneContainer.java | 61 +++ .../StandaloneContainerActivatorTest.java | 5 +- .../standalone/StandaloneContainerTest.java | 74 +++ .../standalone/StandaloneSubscriberTest.java | 52 ++ .../CloudConfigInstallVariablesTest.scala | 59 --- .../container/standalone/StandaloneContainer.scala | 64 --- .../standalone/StandaloneContainerTest.scala | 85 --- .../standalone/StandaloneSubscriberTest.scala | 41 -- 21 files changed, 1387 insertions(+), 918 deletions(-) create mode 100644 standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java create mode 100644 standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java create mode 100644 standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java create mode 100644 standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java create mode 100644 standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java delete mode 100644 standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala delete mode 100644 standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala delete mode 100644 standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala delete mode 100644 standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala delete mode 100644 standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala delete mode 100644 standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala delete mode 100644 standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala create mode 100644 standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java create mode 100644 standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java create mode 100644 standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java create mode 100644 standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java delete mode 100644 standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala delete mode 100644 standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala delete mode 100644 standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala delete mode 100644 standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala (limited to 'standalone-container/src') diff --git a/standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java b/standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java new file mode 100644 index 00000000000..8d4126a01e3 --- /dev/null +++ b/standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java @@ -0,0 +1,569 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.impl; + +import com.google.common.collect.Lists; +import com.yahoo.container.standalone.StandaloneContainerApplication; +import com.yahoo.jdisc.application.OsgiFramework; +import com.yahoo.jdisc.application.OsgiHeader; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkListener; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceObjects; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.Version; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRequirement; +import org.osgi.framework.wiring.BundleRevision; +import org.osgi.framework.wiring.BundleWire; +import org.osgi.framework.wiring.BundleWiring; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; +import org.osgi.resource.Wire; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.Attributes; +import java.util.jar.JarFile; + +/** + * A (mock) OSGI implementation which loads classes from the system classpath + * + * @author Tony Vaagenes + * @author ollivir + */ +public final class ClassLoaderOsgiFramework implements OsgiFramework { + private BundleContextImpl bundleContextImpl = new BundleContextImpl(); + private SystemBundleImpl systemBundleImpl = new SystemBundleImpl(); + private BundleWiringImpl bundleWiringImpl = new BundleWiringImpl(); + + private List bundleLocations = new ArrayList<>(); + private List bundleList = Lists.newArrayList(systemBundleImpl); + private ClassLoader classLoader = null; + + private AtomicInteger nextBundleId = new AtomicInteger(1); + + @Override + public List installBundle(String bundleLocation) { + if (bundleLocation != null && bundleLocation.isEmpty() == false) { + try { + URL url = new URL(bundleLocation); + bundleLocations.add(url); + bundleList.add(new JarBundleImpl(url)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return bundles(); + } + + private ClassLoader getClassLoader() { + if (bundleLocations.isEmpty()) { + return getClass().getClassLoader(); + } else { + if (classLoader == null) { + classLoader = new URLClassLoader(bundleLocations.toArray(new URL[0]), getClass().getClassLoader()); + } + return classLoader; + } + } + + @Override + public void startBundles(List bundles, boolean privileged) { + } + + @Override + public void refreshPackages() { + } + + @Override + public BundleContext bundleContext() { + return bundleContextImpl; + } + + @Override + public List bundles() { + return bundleList; + } + + @Override + public void start() { + } + + @Override + public void stop() { + } + + private abstract class BundleImpl implements Bundle { + @Override + public int getState() { + return Bundle.ACTIVE; + } + + @Override + public void start(int options) { + } + + @Override + public void start() { + } + + @Override + public void stop(int options) { + } + + @Override + public void stop() { + } + + @Override + public void update(InputStream input) { + } + + @Override + public void update() { + } + + @Override + public void uninstall() { + } + + @Override + public Dictionary getHeaders(String locale) { + return getHeaders(); + } + + @Override + public String getSymbolicName() { + return ClassLoaderOsgiFramework.this.getClass().getName(); + } + + @Override + public String getLocation() { + return getSymbolicName(); + } + + @Override + public ServiceReference[] getRegisteredServices() { + return new ServiceReference[0]; + } + + @Override + public ServiceReference[] getServicesInUse() { + return getRegisteredServices(); + } + + @Override + public boolean hasPermission(Object permission) { + return true; + } + + @Override + public URL getResource(String name) { + return getClassLoader().getResource(name); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return getClassLoader().loadClass(name); + } + + @Override + public Enumeration getResources(String name) throws IOException { + return getClassLoader().getResources(name); + } + + @Override + public Enumeration getEntryPaths(String path) { + throw new UnsupportedOperationException(); + } + + @Override + public URL getEntry(String path) { + throw new UnsupportedOperationException(); + } + + @Override + public Enumeration findEntries(String path, String filePattern, boolean recurse) { + throw new UnsupportedOperationException(); + } + + @Override + public long getLastModified() { + return 1L; + } + + @Override + public BundleContext getBundleContext() { + throw new UnsupportedOperationException(); + } + + @Override + public Map> getSignerCertificates(int signersType) { + return Collections.emptyMap(); + } + + @Override + @SuppressWarnings("unchecked") + public T adapt(Class clazz) { + if (clazz.equals(BundleRevision.class)) { + return (T) new BundleRevisionImpl(); + } else if (clazz.equals(BundleWiring.class)) { + return (T) new BundleWiringImpl(); + } else { + return null; + } + } + + @Override + public File getDataFile(String filename) { + return null; + } + + @Override + public int compareTo(Bundle o) { + return Long.compare(getBundleId(), o.getBundleId()); + } + } + + private class BundleRevisionImpl implements BundleRevision { + @Override + public String getSymbolicName() { + return this.getClass().getName(); + } + + @Override + public List getDeclaredRequirements(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public Version getVersion() { + return Version.emptyVersion; + } + + @Override + public BundleWiring getWiring() { + return bundleWiringImpl; + } + + @Override + public List getDeclaredCapabilities(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public int getTypes() { + return 0; + } + + @Override + public Bundle getBundle() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCapabilities(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public List getRequirements(String p1) { + throw new UnsupportedOperationException(); + } + } + + private class BundleWiringImpl implements BundleWiring { + @Override + public List findEntries(String p1, String p2, int p3) { + throw new UnsupportedOperationException(); + } + + @Override + public List getRequiredResourceWires(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public List getResourceCapabilities(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCurrent() { + throw new UnsupportedOperationException(); + } + + @Override + public List getRequiredWires(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public List getCapabilities(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public List getProvidedResourceWires(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public List getProvidedWires(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public BundleRevision getRevision() { + throw new UnsupportedOperationException(); + } + + @Override + public List getResourceRequirements(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isInUse() { + throw new UnsupportedOperationException(); + } + + @Override + public Collection listResources(String p1, String p2, int p3) { + throw new UnsupportedOperationException(); + } + + @Override + public ClassLoader getClassLoader() { + return ClassLoaderOsgiFramework.this.getClassLoader(); + } + + @Override + public List getRequirements(String p1) { + throw new UnsupportedOperationException(); + } + + @Override + public BundleRevision getResource() { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle getBundle() { + throw new UnsupportedOperationException(); + } + } + + private class SystemBundleImpl extends BundleImpl { + @Override + public long getBundleId() { + return 0L; + } + + @Override + public Version getVersion() { + return Version.emptyVersion; + } + + @Override + public Dictionary getHeaders() { + Hashtable ret = new Hashtable<>(); + ret.put(OsgiHeader.APPLICATION, StandaloneContainerApplication.class.getName()); + return ret; + } + } + + private class JarBundleImpl extends BundleImpl { + private final long bundleId; + private final Dictionary headers; + + JarBundleImpl(URL location) throws IOException { + this.bundleId = (long) nextBundleId.getAndIncrement(); + this.headers = retrieveHeaders(location); + } + + @Override + public long getBundleId() { + return bundleId; + } + + @Override + public Dictionary getHeaders() { + return headers; + } + + @Override + public String getSymbolicName() { + return headers.get("Bundle-SymbolicName"); + } + + @Override + public Version getVersion() { + return Version.parseVersion(headers.get("Bundle-Version")); + } + + private Dictionary retrieveHeaders(URL location) throws IOException { + try (JarFile jarFile = new JarFile(location.getFile())) { + Attributes attributes = jarFile.getManifest().getMainAttributes(); + Hashtable ret = new Hashtable<>(); + attributes.forEach((k, v) -> ret.put(k.toString(), v.toString())); + return ret; + } + } + } + + private class BundleContextImpl implements BundleContext { + @Override + public String getProperty(String key) { + return null; + } + + @Override + public Bundle getBundle() { + return systemBundleImpl; + } + + @Override + public Bundle installBundle(String location, InputStream input) { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle installBundle(String location) { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle getBundle(long id) { + return systemBundleImpl; + } + + @Override + public Bundle[] getBundles() { + return new Bundle[] { systemBundleImpl }; + } + + @Override + public Bundle getBundle(String location) { + return systemBundleImpl; + } + + @Override + public void addServiceListener(ServiceListener listener, String filter) { + } + + @Override + public void addServiceListener(ServiceListener listener) { + } + + @Override + public void removeServiceListener(ServiceListener listener) { + } + + @Override + public void addBundleListener(BundleListener listener) { + } + + @Override + public void removeBundleListener(BundleListener listener) { + } + + @Override + public void addFrameworkListener(FrameworkListener listener) { + } + + @Override + public void removeFrameworkListener(FrameworkListener listener) { + } + + @Override + public ServiceRegistration registerService(String[] classes, Object service, Dictionary properties) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) { + return null; + } + + @Override + public ServiceRegistration registerService(Class clazz, S service, Dictionary properties) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceReference[] getServiceReferences(String clazz, String filter) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceReference[] getAllServiceReferences(String clazz, String filter) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceReference getServiceReference(String clazz) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceReference getServiceReference(Class clazz) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection> getServiceReferences(Class clazz, String filter) { + return new ArrayList<>(); + } + + @Override + public S getService(ServiceReference reference) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean ungetService(ServiceReference reference) { + throw new UnsupportedOperationException(); + } + + @Override + public File getDataFile(String filename) { + throw new UnsupportedOperationException(); + } + + @Override + public Filter createFilter(String filter) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceRegistration registerService(Class aClass, ServiceFactory serviceFactory, + Dictionary dictionary) { + throw new UnsupportedOperationException(); + } + + @Override + public ServiceObjects getServiceObjects(ServiceReference serviceReference) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java b/standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java new file mode 100644 index 00000000000..a0fee3265df --- /dev/null +++ b/standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java @@ -0,0 +1,34 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.impl; + +import com.yahoo.text.Utf8; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * @author Tony Vaagenes + * @author ollivir + */ +public class StandaloneContainerRunner { + public static Path createApplicationPackage(String servicesXml) { + try { + return createApplicationDirectory(servicesXml); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static Path createApplicationDirectory(String servicesXml) throws IOException { + Path applicationDir = Files.createTempDirectory("application"); + Path servicesXmlFile = applicationDir.resolve("services.xml"); + String content = servicesXml; + + if (!servicesXml.startsWith("\n" + servicesXml; + } + Files.write(servicesXmlFile, Utf8.toBytes(content)); + return applicationDir; + } +} diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java b/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java new file mode 100644 index 00000000000..4bbe9986d90 --- /dev/null +++ b/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java @@ -0,0 +1,97 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.yahoo.config.FileReference; +import com.yahoo.config.application.api.FileRegistry; +import com.yahoo.filedistribution.fileacquirer.FileAcquirer; +import com.yahoo.net.HostName; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * FileAcquirer and FileRegistry working on a local directory. + * + * @author Tony Vaagenes + * @author ollivir + */ +public class LocalFileDb implements FileAcquirer, FileRegistry { + private static final Constructor fileReferenceConstructor = createFileReferenceConstructor(); + + private final Map fileReferenceToFile = new HashMap<>(); + private final Path appPath; + + public LocalFileDb(Path appPath) { + this.appPath = appPath; + } + + /* FileAcquirer overrides */ + @Override + public File waitFor(FileReference reference, long l, TimeUnit timeUnit) { + synchronized (this) { + File file = fileReferenceToFile.get(reference); + if (file == null) { + throw new RuntimeException("Invalid file reference " + reference); + } + return file; + } + } + + @Override + public void shutdown() { + } + + /* FileRegistry overrides */ + public FileReference addFile(String relativePath) { + File file = appPath.resolve(relativePath).toFile(); + if (!file.exists()) { + throw new RuntimeException("The file does not exist: " + file.getPath()); + } + + FileReference fileReference = null; + try { + fileReference = fileReferenceConstructor.newInstance("LocalFileDb:" + relativePath); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Unable to create new FileReference", e); + } + fileReferenceToFile.put(fileReference, file); + return fileReference; + } + + @Override + public List export() { + return fileReferenceToFile.entrySet().stream().map(entry -> new Entry(entry.getValue().getPath(), entry.getKey())) + .collect(Collectors.toList()); + } + + @Override + public FileReference addUri(String uri) { + throw new RuntimeException("addUri(String uri) is not implemented here."); + } + + public String fileSourceHost() { + return HostName.getLocalhost(); + } + + public Set allRelativePaths() { + return fileReferenceToFile.values().stream().map(File::getPath).collect(Collectors.toSet()); + } + + private static Constructor createFileReferenceConstructor() { + try { + Constructor method = FileReference.class.getDeclaredConstructor(String.class); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ex) { + throw new IllegalStateException(ex); + } + } +} diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java new file mode 100644 index 00000000000..72937301954 --- /dev/null +++ b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java @@ -0,0 +1,304 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.google.inject.AbstractModule; +import com.google.inject.ConfigurationException; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.ProvisionException; +import com.google.inject.name.Named; +import com.google.inject.name.Names; +import com.yahoo.collections.Pair; +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.model.ApplicationConfigProducerRoot; +import com.yahoo.config.model.ConfigModelRepo; +import com.yahoo.config.model.application.provider.BaseDeployLogger; +import com.yahoo.config.model.application.provider.FilesApplicationPackage; +import com.yahoo.config.model.application.provider.StaticConfigDefinitionRepo; +import com.yahoo.config.model.builder.xml.ConfigModelId; +import com.yahoo.config.model.builder.xml.XmlHelper; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.provision.Zone; +import com.yahoo.container.di.config.SubscriberFactory; +import com.yahoo.container.jdisc.ConfiguredApplication; +import com.yahoo.io.IOUtils; +import com.yahoo.jdisc.application.Application; +import com.yahoo.text.XML; +import com.yahoo.vespa.defaults.Defaults; +import com.yahoo.vespa.model.HostResource; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder; +import com.yahoo.vespa.model.container.Container; +import com.yahoo.vespa.model.container.ContainerModel; +import com.yahoo.vespa.model.container.xml.ConfigServerContainerModelBuilder; +import com.yahoo.vespa.model.container.xml.ContainerModelBuilder; +import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking; +import org.w3c.dom.Element; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.yahoo.collections.CollectionUtil.first; + +/** + * @author Tony Vaagenes + * @author gjoranv + * @author ollivir + */ +public class StandaloneContainerApplication implements Application { + public static final String PACKAGE_NAME = "standalone_jdisc_container"; + public static final String APPLICATION_LOCATION_INSTALL_VARIABLE = PACKAGE_NAME + ".app_location"; + public static final String DEPLOYMENT_PROFILE_INSTALL_VARIABLE = PACKAGE_NAME + ".deployment_profile"; + public static final String DISABLE_NETWORKING_ANNOTATION = "JDisc.disableNetworking"; + public static final Named APPLICATION_PATH_NAME = Names.named(APPLICATION_LOCATION_INSTALL_VARIABLE); + public static final Named CONFIG_MODEL_REPO_NAME = Names.named("ConfigModelRepo"); + + private static final String DEFAULT_TMP_BASE_DIR = Defaults.getDefaults().underVespaHome("tmp"); + private static final String TMP_DIR_NAME = "standalone_container"; + + private static final StaticConfigDefinitionRepo configDefinitionRepo = new StaticConfigDefinitionRepo(); + + private final Injector injector; + private final Path applicationPath; + private final LocalFileDb distributedFiles; + private final ConfigModelRepo configModelRepo; + private final Networking networkingOption; + private final VespaModel modelRoot; + private final Application configuredApplication; + private final Container container; + + @Inject + public StandaloneContainerApplication(Injector injector) { + this.injector = injector; + ConfiguredApplication.ensureVespaLoggingInitialized(); + this.applicationPath = injectedApplicationPath().orElseGet(this::installApplicationPath); + this.distributedFiles = new LocalFileDb(applicationPath); + this.configModelRepo = resolveConfigModelRepo(); + this.networkingOption = resolveNetworkingOption(); + + try { + Pair tpl = withTempDir(preprocessedApplicationDir -> createContainerModel(applicationPath, + distributedFiles, preprocessedApplicationDir, networkingOption, configModelRepo)); + this.modelRoot = tpl.getFirst(); + this.container = tpl.getSecond(); + } catch (RuntimeException r) { + throw r; + } catch (Exception e) { + throw new RuntimeException("Failed to create ContainerModel", e); + } + this.configuredApplication = createConfiguredApplication(container); + } + + private ConfigModelRepo resolveConfigModelRepo() { + try { + return injector.getInstance(Key.get(ConfigModelRepo.class, CONFIG_MODEL_REPO_NAME)); + } catch (Exception e) { + return new ConfigModelRepo(); + } + } + + private Networking resolveNetworkingOption() { + try { + Boolean networkingDisable = injector.getInstance(Key.get(Boolean.class, Names.named(DISABLE_NETWORKING_ANNOTATION))); + if (networkingDisable != null) { + return networkingDisable ? Networking.disable : Networking.enable; + } + } catch (Exception ignored) { + } + return Networking.enable; + } + + private Application createConfiguredApplication(Container container) { + Injector augmentedInjector = injector.createChildInjector(new AbstractModule() { + @Override + public void configure() { + bind(SubscriberFactory.class).toInstance(new StandaloneSubscriberFactory(modelRoot)); + } + }); + + System.setProperty("config.id", container.getConfigId()); + return augmentedInjector.getInstance(ConfiguredApplication.class); + } + + private Optional injectedApplicationPath() { + try { + return Optional.ofNullable(injector.getInstance(Key.get(Path.class, APPLICATION_PATH_NAME))); + } catch (ConfigurationException | ProvisionException ignored) { + } + return Optional.empty(); + } + + private Path installApplicationPath() { + Optional variable = optionalInstallVariable(APPLICATION_LOCATION_INSTALL_VARIABLE); + + return variable.map(Paths::get) + .orElseThrow(() -> new IllegalStateException("Environment variable not set: " + APPLICATION_LOCATION_INSTALL_VARIABLE)); + } + + @Override + public void start() { + try { + com.yahoo.container.Container.get().setCustomFileAcquirer(distributedFiles); + configuredApplication.start(); + } catch (Exception e) { + com.yahoo.container.Container.resetInstance(); + throw e; + } + } + + @Override + public void stop() { + configuredApplication.stop(); + } + + @Override + public void destroy() { + com.yahoo.container.Container.resetInstance(); + configuredApplication.destroy(); + } + + public Container container() { + return container; + } + + private interface ThrowingFunction { + U apply(T input) throws Exception; + } + + private static T withTempDir(ThrowingFunction f) throws Exception { + File tmpDir = createTempDir(); + try { + return f.apply(tmpDir); + } finally { + IOUtils.recursiveDeleteDir(tmpDir); + } + } + + private static File createTempDir() { + Path basePath; + if (new File(DEFAULT_TMP_BASE_DIR).exists()) { + basePath = Paths.get(DEFAULT_TMP_BASE_DIR); + } else { + basePath = Paths.get(System.getProperty("java.io.tmpdir")); + } + + try { + Path tmpDir = Files.createTempDirectory(basePath, TMP_DIR_NAME); + return tmpDir.toFile(); + } catch (IOException e) { + throw new RuntimeException("Cannot create temp directory", e); + } + } + + private static void validateApplication(ApplicationPackage applicationPackage) { + try { + applicationPackage.validateXML(); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + private static ContainerModelBuilder newContainerModelBuilder(Networking networkingOption) { + Optional profile = optionalInstallVariable(DEPLOYMENT_PROFILE_INSTALL_VARIABLE); + if (profile.isPresent()) { + String profileName = profile.get(); + if ("configserver".equals(profileName)) { + return new ConfigServerContainerModelBuilder(new CloudConfigInstallVariables()); + } else { + throw new RuntimeException("Invalid deployment profile '" + profileName + "'"); + } + } else { + return new ContainerModelBuilder(true, networkingOption); + } + } + + static Pair createContainerModel(Path applicationPath, FileRegistry fileRegistry, + File preprocessedApplicationDir, Networking networkingOption, ConfigModelRepo configModelRepo) throws Exception { + DeployLogger logger = new BaseDeployLogger(); + FilesApplicationPackage rawApplicationPackage = new FilesApplicationPackage.Builder(applicationPath.toFile()) + .includeSourceFiles(true).preprocessedDir(preprocessedApplicationDir).build(); + ApplicationPackage applicationPackage = rawApplicationPackage.preprocess(Zone.defaultZone(), logger); + validateApplication(applicationPackage); + DeployState deployState = new DeployState.Builder().applicationPackage(applicationPackage).fileRegistry(fileRegistry) + .deployLogger(logger).configDefinitionRepo(configDefinitionRepo).build(true); + + VespaModel root = VespaModel.createIncomplete(deployState); + ApplicationConfigProducerRoot vespaRoot = new ApplicationConfigProducerRoot(root, "vespa", deployState.getDocumentModel(), + deployState.getProperties().vespaVersion(), deployState.getProperties().applicationId()); + + Element spec = containerRootElement(applicationPackage); + ContainerModel containerModel = newContainerModelBuilder(networkingOption).build(deployState, configModelRepo, vespaRoot, spec); + containerModel.getCluster().prepare(); + initializeContainerModel(containerModel, configModelRepo); + Container container = first(containerModel.getCluster().getContainers()); + + // TODO: Separate out model finalization from the VespaModel constructor, + // such that the above and below code to finalize the container can be + // replaced by root.finalize(); + + initializeContainer(container, spec); + + root.freezeModelTopology(); + return new Pair<>(root, container); + } + + private static void initializeContainer(Container container, Element spec) { + HostResource host = container.getRoot().getHostSystem().getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC); + + container.setBasePort(VespaDomBuilder.getXmlWantedPort(spec)); + container.setHostResource(host); + container.initService(); + } + + private static Element getJDiscInServices(Element element) { + List jDiscElements = new ArrayList<>(); + for (ConfigModelId cid : ContainerModelBuilder.configModelIds) { + List children = XML.getChildren(element, cid.getName()); + jDiscElements.addAll(children); + } + + if (jDiscElements.size() == 1) { + return jDiscElements.get(0); + } else if (jDiscElements.isEmpty()) { + throw new RuntimeException("No jdisc element found under services."); + } else { + List nameAndId = jDiscElements.stream().map(e -> e.getNodeName() + " id='" + e.getAttribute("id") + "'") + .collect(Collectors.toList()); + throw new RuntimeException("Found multiple JDisc elements: " + String.join(", ", nameAndId)); + } + } + + private static Element containerRootElement(ApplicationPackage applicationPackage) { + Element element = XmlHelper.getDocument(applicationPackage.getServices()).getDocumentElement(); + String nodeName = element.getNodeName(); + + if (ContainerModelBuilder.configModelIds.stream().anyMatch(id -> id.getName().equals(nodeName))) { + return element; + } else { + return getJDiscInServices(element); + } + } + + @SuppressWarnings("deprecation") // TODO: what is the not-deprecated way? + private static void initializeContainerModel(ContainerModel containerModel, ConfigModelRepo configModelRepo) { + containerModel.initialize(configModelRepo); + } + + private static Optional optionalInstallVariable(String name) { + Optional fromEnv = Optional.ofNullable(System.getenv((name.replace(".", "__")))); + if (fromEnv.isPresent()) { + return fromEnv; + } + return Optional.ofNullable(System.getProperty(name)); // for unit testing + } +} diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java new file mode 100644 index 00000000000..882bb5709f4 --- /dev/null +++ b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java @@ -0,0 +1,127 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.yahoo.config.ConfigBuilder; +import com.yahoo.config.ConfigInstance; +import com.yahoo.config.subscription.ConfigInterruptedException; +import com.yahoo.container.di.config.Subscriber; +import com.yahoo.container.di.config.SubscriberFactory; +import com.yahoo.vespa.config.ConfigKey; +import com.yahoo.vespa.model.VespaModel; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author Tony Vaagenes + * @author gjoranv + * @author ollivir + */ +public class StandaloneSubscriberFactory implements SubscriberFactory { + private final VespaModel root; + + public StandaloneSubscriberFactory(VespaModel root) { + this.root = root; + } + + private class StandaloneSubscriber implements Subscriber { + private final Set> configKeys; + private long generation = -1L; + + StandaloneSubscriber(Set> configKeys) { + this.configKeys = configKeys; + } + + @Override + public boolean configChanged() { + return generation == 0; + } + + @Override + public void close() { + } + + @Override + public Map, ConfigInstance> config() { + Map, ConfigInstance> ret = new HashMap<>(); + for (ConfigKey key : configKeys) { + ConfigInstance.Builder builder = root.getConfig(newBuilderInstance(key), key.getConfigId()); + if (builder == null) { + throw new RuntimeException("Invalid config id " + key.getConfigId()); + } + ret.put(key, newConfigInstance(builder)); + } + return ret; + } + + @Override + public long waitNextGeneration() { + generation++; + + if (generation != 0) { + try { + while (!Thread.interrupted()) { + Thread.sleep(10000); + } + } catch (InterruptedException e) { + throw new ConfigInterruptedException(e); + } + } + + return generation; + } + + // if waitNextGeneration has not yet been called, -1 should be returned + @Override + public long generation() { + return generation; + } + } + + @Override + @SuppressWarnings("unchecked") + public Subscriber getSubscriber(Set> configKeys) { + return new StandaloneSubscriber((Set>) configKeys); + } + + public void reloadActiveSubscribers(long generation) { + throw new RuntimeException("unsupported"); + } + + private static ConfigInstance.Builder newBuilderInstance(ConfigKey key) { + try { + return builderClass(key).getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException("ConfigInstance builder cannot be instantiated", e); + } + } + + @SuppressWarnings("unchecked") + private static Class builderClass(ConfigKey key) { + Class configClass = key.getConfigClass(); + if (configClass != null) { + Class[] nestedClasses = configClass.getClasses(); + for (Class clazz : nestedClasses) { + if (clazz.getName().equals(key.getConfigClass().getName() + "$Builder")) { + return (Class) clazz; + } + } + } + throw new RuntimeException("Builder class for " + (configClass == null ? null : configClass.getName()) + " could not be located"); + } + + private static ConfigInstance newConfigInstance(ConfigBuilder builder) { + try { + return configClass(builder).getConstructor(builder.getClass()).newInstance(builder); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException("ConfigInstance cannot be instantiated", e); + } + } + + @SuppressWarnings("unchecked") + private static Class configClass(ConfigBuilder builder) { + return (Class) builder.getClass().getEnclosingClass(); + } +} diff --git a/standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala b/standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala deleted file mode 100644 index ac8636de2cb..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.application.container.impl - -import java.io.InputStream -import java.net.{URL, URLClassLoader} -import java.util -import java.util.concurrent.atomic.AtomicInteger -import java.util.jar.JarFile -import java.util.{Collections, Dictionary, Hashtable} - -import com.yahoo.container.standalone.StandaloneContainerApplication -import com.yahoo.jdisc.application.{OsgiFramework, OsgiHeader} -import org.osgi.framework._ -import org.osgi.framework.wiring._ -import org.osgi.resource.{Capability, Requirement, Wire} - -import scala.collection.JavaConverters._ -import scala.collection.mutable.ArrayBuffer - -/** - * A (mock) OSGI implementation which loads classes from the system classpath - * - * @author tonytv - */ -final class ClassLoaderOsgiFramework extends OsgiFramework { - private val bundleLocations = new ArrayBuffer[URL] - private val bundleList = ArrayBuffer[Bundle](SystemBundleImpl) - private var classLoader: ClassLoader = null - - private val nextBundleId = new AtomicInteger(1) - - override def installBundle(bundleLocation: String) = { - if (bundleLocation != "") { - val url = new URL(bundleLocation) - bundleLocations += url - bundleList += new JarBundleImpl(url) - } - - bundles() - } - - def getClassLoader = { - if (bundleLocations.isEmpty) { - getClass.getClassLoader - } else { - if(classLoader == null) - classLoader = new URLClassLoader(bundleLocations.toArray, getClass.getClassLoader) - - classLoader - } - } - - override def startBundles(bundles: util.List[Bundle], privileged: Boolean) {} - - override def refreshPackages() {} - - override def bundleContext():BundleContext = BundleContextImpl - - override def bundles() = bundleList.asJava - - override def start() {} - - override def stop() {} - - private abstract class BundleImpl extends Bundle { - override def getState = Bundle.ACTIVE - - override def start(options: Int) {} - override def start() {} - override def stop(options: Int) {} - override def stop() {} - override def update(input: InputStream) {} - override def update() {} - override def uninstall() {} - - override def getHeaders(locale: String) = getHeaders - - override def getSymbolicName = ClassLoaderOsgiFramework.this.getClass.getName - override def getLocation = getSymbolicName - - override def getRegisteredServices = Array[ServiceReference[_]]() - override def getServicesInUse = getRegisteredServices - - override def hasPermission(permission: Any) = true - - override def getResource(name: String) = getClassLoader.getResource(name) - override def loadClass(name: String) = getClassLoader.loadClass(name) - override def getResources(name: String) = getClassLoader.getResources(name) - - override def getEntryPaths(path: String) = throw new UnsupportedOperationException - override def getEntry(path: String) = throw new UnsupportedOperationException - override def findEntries(path: String, filePattern: String, recurse: Boolean) = throw new UnsupportedOperationException - - override def getLastModified = 1L - - override def getBundleContext = throw new UnsupportedOperationException - override def getSignerCertificates(signersType: Int) = Collections.emptyMap() - - override def adapt[A](`type`: Class[A]): A = { - if (`type` == classOf[BundleRevision]) BundleRevisionImpl.asInstanceOf[A] - else if (`type` == classOf[BundleWiring]) BundleWiringImpl.asInstanceOf[A] - else null.asInstanceOf[A] - } - - override def getDataFile(filename: String) = null - override def compareTo(o: Bundle) = getBundleId compareTo o.getBundleId - } - - private object BundleRevisionImpl extends BundleRevision { - override def getSymbolicName: String = this.getClass.getName - override def getDeclaredRequirements(p1: String): util.List[BundleRequirement] = throw new UnsupportedOperationException - override def getVersion: Version = Version.emptyVersion - override def getWiring: BundleWiring = BundleWiringImpl - override def getDeclaredCapabilities(p1: String): util.List[BundleCapability] = throw new UnsupportedOperationException - override def getTypes: Int = 0 - override def getBundle: Bundle = throw new UnsupportedOperationException - override def getCapabilities(p1: String): util.List[Capability] = throw new UnsupportedOperationException - override def getRequirements(p1: String): util.List[Requirement] = throw new UnsupportedOperationException - } - - private object BundleWiringImpl extends BundleWiring { - override def findEntries(p1: String, p2: String, p3: Int): util.List[URL] = ??? - override def getRequiredResourceWires(p1: String): util.List[Wire] = ??? - override def getResourceCapabilities(p1: String): util.List[Capability] = ??? - override def isCurrent: Boolean = ??? - override def getRequiredWires(p1: String): util.List[BundleWire] = ??? - override def getCapabilities(p1: String): util.List[BundleCapability] = ??? - override def getProvidedResourceWires(p1: String): util.List[Wire] = ??? - override def getProvidedWires(p1: String): util.List[BundleWire] = ??? - override def getRevision: BundleRevision = ??? - override def getResourceRequirements(p1: String): util.List[Requirement] = ??? - override def isInUse: Boolean = ??? - override def listResources(p1: String, p2: String, p3: Int): util.Collection[String] = ??? - override def getClassLoader: ClassLoader = ClassLoaderOsgiFramework.this.getClassLoader - override def getRequirements(p1: String): util.List[BundleRequirement] = ??? - override def getResource: BundleRevision = ??? - override def getBundle: Bundle = ??? - } - - private object SystemBundleImpl extends BundleImpl { - override val getBundleId = 0L - override def getVersion = Version.emptyVersion - override def getHeaders: Dictionary[String, String] = new Hashtable[String, String](Map(OsgiHeader.APPLICATION -> classOf[StandaloneContainerApplication].getName).asJava) - } - - - private class JarBundleImpl(location: URL) extends BundleImpl { - override val getBundleId = nextBundleId.getAndIncrement.asInstanceOf[Long] - - private val headers = retrieveHeaders(location) - - override def getHeaders: Dictionary[String, String] = headers - override val getSymbolicName = headers.get("Bundle-SymbolicName") - override val getVersion = Version.parseVersion(headers.get("Bundle-Version")) - - - private def retrieveHeaders(location: URL) = { - val jarFile = new JarFile(location.getFile) - try { - val attributes = jarFile.getManifest.getMainAttributes - new Hashtable[String, String](attributes.entrySet().asScala.map( entry => entry.getKey.toString -> entry.getValue.toString).toMap.asJava) - } finally { - jarFile.close() - } - } - } - - private object BundleContextImpl extends BundleContext { - private val bundleImpl = SystemBundleImpl - - override def getProperty(key: String) = null - override def getBundle = bundleImpl - override def installBundle(location: String, input: InputStream) = throw new UnsupportedOperationException - override def installBundle(location: String) = throw new UnsupportedOperationException - - override def getBundle(id: Long) = bundleImpl - override def getBundles = Array(bundleImpl) - override def getBundle(location: String) = bundleImpl - - override def addServiceListener(listener: ServiceListener, filter: String) {} - override def addServiceListener(listener: ServiceListener) {} - override def removeServiceListener(listener: ServiceListener) {} - override def addBundleListener(listener: BundleListener) {} - override def removeBundleListener(listener: BundleListener) {} - - override def addFrameworkListener(listener: FrameworkListener) {} - override def removeFrameworkListener(listener: FrameworkListener) {} - - override def registerService(clazzes: Array[String], service: Any, properties: Dictionary[String, _]) = throw new UnsupportedOperationException - override def registerService(clazz: String, service: Any, properties: Dictionary[String, _]) = null - override def registerService[S](clazz: Class[S], service: S, properties: Dictionary[String, _]) = throw new UnsupportedOperationException - override def getServiceReferences(clazz: String, filter: String) = throw new UnsupportedOperationException - override def getAllServiceReferences(clazz: String, filter: String) = throw new UnsupportedOperationException - override def getServiceReference(clazz: String) = throw new UnsupportedOperationException - override def getServiceReference[S](clazz: Class[S]) = throw new UnsupportedOperationException - override def getServiceReferences[S](clazz: Class[S], filter: String) = Collections.emptyList() - override def getService[S](reference: ServiceReference[S]) = throw new UnsupportedOperationException - override def ungetService(reference: ServiceReference[_]) = throw new UnsupportedOperationException - override def getDataFile(filename: String) = throw new UnsupportedOperationException - override def createFilter(filter: String) = throw new UnsupportedOperationException - - override def registerService[S](aClass: Class[S], serviceFactory: ServiceFactory[S], dictionary: Dictionary[String, _]): ServiceRegistration[S] = throw new UnsupportedOperationException - override def getServiceObjects[S](serviceReference: ServiceReference[S]): ServiceObjects[S] = throw new UnsupportedOperationException - } - -} diff --git a/standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala b/standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala deleted file mode 100644 index 91634250fc5..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.application.container.impl - -import java.nio.file.Files -import com.yahoo.text.Utf8 - -/** - * @author tonytv - */ -final class StandaloneContainerRunner { - - - -} - -object StandaloneContainerRunner { - def createApplicationPackage(servicesXml: String) = { - val applicationDir = Files.createTempDirectory("application") - - val servicesXmlFile = applicationDir.resolve("services.xml"); - var content = servicesXml; - if ( ! servicesXml.startsWith("""" + '\n' + servicesXml - Files.write(servicesXmlFile, Utf8.toBytes(content)) - applicationDir - } -} diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala deleted file mode 100644 index 61128347319..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -/** - * @author tonytv - */ -trait Converter[T] { - def convert(s: String): T -} - -object Converter { - def toConverter[T](f: String => T) = new Converter[T] { - override def convert(s: String) = f(s) - } - - implicit val intConverter = toConverter(_.toInt) - implicit val longConverter = toConverter(_.toLong) - implicit val boolConverter = toConverter(_.toBoolean) - implicit val stringConverter = toConverter(identity) - - implicit val javaIntegerConverter:Converter[Integer] = toConverter(_.toInt) - implicit val javaLongConverter:Converter[java.lang.Long] = toConverter(_.toLong) - implicit val javaBooleanConverter:Converter[java.lang.Boolean] = toConverter(_.toBoolean) - - -} diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala deleted file mode 100644 index 2aab88d8319..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -/** - * @author tonytv - * TODO: copied from standalone-container. Move to separate lib module instead. - */ -object Environment { - def optionalInstallVariable(name: String) = { - env(name.replace(".", "__")). - orElse(systemProperty(name)) //for unit testing - } - - def installVariable(name: String) = { - optionalInstallVariable(name). - getOrElse { - throw new IllegalStateException("Environment variable not set: " + name) - } - } - - def env(name: String) = Option(System.getenv(name)) - def systemProperty(name: String) = Option(System.getProperty(name)) -} diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala deleted file mode 100644 index 6507b4c72f0..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -import java.io.File -import java.lang.reflect.Constructor -import java.nio.file.Path -import java.util -import java.util.concurrent.TimeUnit - -import com.yahoo.config.FileReference -import com.yahoo.config.application.api.FileRegistry -import com.yahoo.config.application.api.FileRegistry.Entry -import com.yahoo.container.standalone.LocalFileDb._ -import com.yahoo.filedistribution.fileacquirer.FileAcquirer -import com.yahoo.net.HostName - -import scala.collection.JavaConverters._ -import scala.collection.mutable - - -/** - * FileAcquirer and FileRegistry working on a local directory. - * @author tonytv - */ -class LocalFileDb(appPath: Path) extends FileAcquirer with FileRegistry { - private val fileReferenceToFile = mutable.Map[FileReference, File]() - - /** *** FileAcquirer overrides *****/ - def waitFor(reference: FileReference, l: Long, timeUnit: TimeUnit): File = { - synchronized { - fileReferenceToFile.get(reference).getOrElse { - throw new RuntimeException("Invalid file reference " + reference) - } - } - } - - override def shutdown() {} - - /** *** FileRegistry overrides *****/ - def addFile(relativePath: String): FileReference = { - val file = appPath.resolve(relativePath).toFile - if (!file.exists) { - throw new RuntimeException("The file does not exist: " + file.getPath) - } - - val fileReference: FileReference = fileReferenceConstructor.newInstance("LocalFileDb:" + relativePath) - fileReferenceToFile.put(fileReference, file) - fileReference - } - - def fileSourceHost: String = - HostName.getLocalhost - - def allRelativePaths: java.util.Set[String] = { - new java.util.HashSet(fileReferenceToFile.values.map(_.getPath).asJavaCollection) - } - - override def export(): util.List[Entry] = { - new java.util.ArrayList(fileReferenceToFile.keys.map{ (ref: FileReference) => new Entry(fileReferenceToFile.get(ref).get.getPath, ref)}.asJavaCollection) - } - - override def addUri(uri: String): FileReference = { - throw new RuntimeException("addUri(uri: String) is not implemented here."); - } -} - -object LocalFileDb { - private def createFileReferenceConstructor: Constructor[FileReference] = { - val method: Constructor[FileReference] = classOf[FileReference].getDeclaredConstructor(classOf[String]) - method.setAccessible(true) - method - } - - private val fileReferenceConstructor: Constructor[FileReference] = createFileReferenceConstructor -} diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala deleted file mode 100644 index 5271cd400d4..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -import java.io.{File, IOException} -import java.lang.{Boolean => JBoolean} -import java.nio.file.{FileSystems, Files, Path, Paths} - -import com.google.inject.name.Names -import com.google.inject.{AbstractModule, Inject, Injector, Key} -import com.yahoo.collections.CollectionUtil.first -import com.yahoo.config.application.api.{ApplicationPackage, FileRegistry} -import com.yahoo.config.model.application.provider._ -import com.yahoo.config.model.builder.xml.XmlHelper -import com.yahoo.config.model.deploy.DeployState -import com.yahoo.config.model.{ApplicationConfigProducerRoot, ConfigModelRepo} -import com.yahoo.config.provision.Zone -import com.yahoo.container.di.config.SubscriberFactory -import com.yahoo.container.jdisc.ConfiguredApplication -import com.yahoo.container.standalone.Environment._ -import com.yahoo.container.standalone.StandaloneContainerApplication._ -import com.yahoo.io.IOUtils -import com.yahoo.jdisc.application.Application -import com.yahoo.text.XML -import com.yahoo.vespa.defaults.Defaults -import com.yahoo.vespa.model.VespaModel -import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder -import com.yahoo.vespa.model.container.{Container, ContainerModel} -import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking -import com.yahoo.vespa.model.container.xml.{ConfigServerContainerModelBuilder, ContainerModelBuilder} -import org.w3c.dom.Element - -import scala.collection.JavaConverters._ -import scala.util.Try - -/** - * @author Tony Vaagenes - * @author gjoranv - */ -class StandaloneContainerApplication @Inject()(injector: Injector) extends Application { - - ConfiguredApplication.ensureVespaLoggingInitialized() - - val applicationPath: Path = injectedApplicationPath.getOrElse(installApplicationPath) - - val distributedFiles = new LocalFileDb(applicationPath) - - val configModelRepo = Try { injector.getInstance(Key.get(classOf[ConfigModelRepo], configModelRepoName))}.getOrElse(new ConfigModelRepo) - - val networkingOption = Try { - injector.getInstance(Key.get(classOf[JBoolean], Names.named(disableNetworkingAnnotation))) - }.map { - case JBoolean.TRUE => Networking.disable - case JBoolean.FALSE => Networking.enable - }.getOrElse(Networking.enable) - - val (modelRoot, container) = withTempDir( - preprocessedApplicationDir => createContainerModel(applicationPath, distributedFiles, preprocessedApplicationDir, networkingOption, configModelRepo)) - - val configuredApplication = createConfiguredApplication(container) - - def createConfiguredApplication(container: Container): Application = { - val augmentedInjector = injector.createChildInjector(new AbstractModule { - def configure() { - bind(classOf[SubscriberFactory]).toInstance(new StandaloneSubscriberFactory(modelRoot)) - } - }) - - System.setProperty("config.id", container.getConfigId) //TODO: DRY - augmentedInjector.getInstance(classOf[ConfiguredApplication]) - } - - def injectedApplicationPath = Try { - injector.getInstance(Key.get(classOf[Path], applicationPathName)) - }.toOption - - def installApplicationPath = path(installVariable(applicationLocationInstallVariable)) - - override def start() { - try { - com.yahoo.container.Container.get().setCustomFileAcquirer(distributedFiles) - configuredApplication.start() - } - catch { - case e: Exception => { com.yahoo.container.Container.resetInstance(); throw e; } - } - } - - override def stop() { - configuredApplication.stop() - } - - override def destroy() { - com.yahoo.container.Container.resetInstance() - configuredApplication.destroy() - } -} - -object StandaloneContainerApplication { - val packageName = "standalone_jdisc_container" - val applicationLocationInstallVariable = s"$packageName.app_location" - val deploymentProfileInstallVariable = s"$packageName.deployment_profile" - - val applicationPathName = Names.named(applicationLocationInstallVariable) - - val disableNetworkingAnnotation = "JDisc.disableNetworking" - val configModelRepoName = Names.named("ConfigModelRepo") - val configDefinitionRepo = new StaticConfigDefinitionRepo() - - val defaultTmpBaseDir = Defaults.getDefaults().underVespaHome("tmp") - val tmpDirName = "standalone_container" - - private def withTempDir[T](f: File => T): T = { - val tmpDir = createTempDir() - try { - f(tmpDir) - } finally { - IOUtils.recursiveDeleteDir(tmpDir) - } - } - - private def createTempDir(): File = { - def getBaseDir: Path = { - val tmpBaseDir = - if (new File(defaultTmpBaseDir).exists()) - defaultTmpBaseDir - else - System.getProperty("java.io.tmpdir") - - Paths.get(tmpBaseDir) - } - - val basePath: Path = getBaseDir - val tmpDir = Files.createTempDirectory(basePath, tmpDirName) - tmpDir.toFile - } - - private def validateApplication(applicationPackage: ApplicationPackage) = { - try { - applicationPackage.validateXML() - } catch { - case e: IOException => throw new IllegalArgumentException(e) - } - } - - def newContainerModelBuilder(networkingOption: Networking): ContainerModelBuilder = { - optionalInstallVariable(deploymentProfileInstallVariable) match { - case None => new ContainerModelBuilder(true, networkingOption) - case Some("configserver") => new ConfigServerContainerModelBuilder(new CloudConfigInstallVariables) - case profileName => throw new RuntimeException(s"Invalid deployment profile '$profileName'") - } - } - - def createContainerModel(applicationPath: Path, - fileRegistry: FileRegistry, - preprocessedApplicationDir: File, - networkingOption: Networking, - configModelRepo: ConfigModelRepo = new ConfigModelRepo): (VespaModel, Container) = { - val logger = new BaseDeployLogger - val rawApplicationPackage = new FilesApplicationPackage.Builder(applicationPath.toFile).includeSourceFiles(true).preprocessedDir(preprocessedApplicationDir).build() - val applicationPackage = rawApplicationPackage.preprocess(Zone.defaultZone(), logger) - validateApplication(applicationPackage) - val deployState = new DeployState.Builder(). - applicationPackage(applicationPackage). - fileRegistry(fileRegistry). - deployLogger(logger). - configDefinitionRepo(configDefinitionRepo). - build(true) - - val root = VespaModel.createIncomplete(deployState) - val vespaRoot = new ApplicationConfigProducerRoot(root, - "vespa", - deployState.getDocumentModel, - deployState.getProperties.vespaVersion(), - deployState.getProperties.applicationId()) - - val spec = containerRootElement(applicationPackage) - val containerModel = newContainerModelBuilder(networkingOption).build(deployState, configModelRepo, vespaRoot, spec) - containerModel.getCluster().prepare() - DeprecationSuppressor.initializeContainerModel(containerModel, configModelRepo) - val container = first(containerModel.getCluster().getContainers) - - // TODO: Separate out model finalization from the VespaModel constructor, - // such that the above and below code to finalize the container can be - // replaced by root.finalize(); - - initializeContainer(container, spec) - - root.freezeModelTopology() - (root, container) - } - - def initializeContainer(container: Container, spec: Element) { - val host = container.getRoot.getHostSystem.getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC) - - container.setBasePort(VespaDomBuilder.getXmlWantedPort(spec)) - container.setHostResource(host) - container.initService() - } - - def getJDiscInServices(element: Element): Element = { - def nameAndId(elements: List[Element]): List[String] = { - elements map { e => s"${e.getNodeName} id='${e.getAttribute("id")}'" } - } - - val jDiscElements = ContainerModelBuilder.configModelIds.asScala flatMap { name => XML.getChildren(element, name.getName).asScala } - jDiscElements.toList match { - case List(e) => e - case Nil => throw new RuntimeException("No jdisc element found under services.") - case multipleElements: List[Element] => throw new RuntimeException("Found multiple JDisc elements: " + nameAndId(multipleElements).mkString(", ")) - } - } - - def containerRootElement(applicationPackage: ApplicationPackage) : Element = { - val element = XmlHelper.getDocument(applicationPackage.getServices).getDocumentElement - val nodeName = element.getNodeName - - if (ContainerModelBuilder.configModelIds.asScala.map(_.getName).contains(nodeName)) element - else getJDiscInServices(element) - } - - def path(s: String) = FileSystems.getDefault.getPath(s) - - // Ugly hack required since Scala cannot suppress warnings. https://issues.scala-lang.org/browse/SI-7934 - private object DeprecationSuppressor extends DeprecationSuppressor - @deprecated("", "") - private class DeprecationSuppressor { - def initializeContainerModel(containerModel: ContainerModel, configModelRepo: ConfigModelRepo): Unit = { - containerModel.initialize(configModelRepo) - } - } -} diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala deleted file mode 100644 index 99cc8259ab3..00000000000 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -import com.yahoo.config.{ConfigBuilder, ConfigInstance} -import com.yahoo.container.di.ConfigKeyT -import com.yahoo.container.di.config.{Subscriber, SubscriberFactory} -import com.yahoo.container.standalone.StandaloneSubscriberFactory._ -import com.yahoo.vespa.config.ConfigKey -import com.yahoo.vespa.model.VespaModel - -import scala.collection.JavaConverters._ - -/** - * @author tonytv - * @author gjoranv - */ -class StandaloneSubscriberFactory(root: VespaModel) extends SubscriberFactory { - class StandaloneSubscriber(configKeys: Set[ConfigKeyT]) extends Subscriber { - override def configChanged = - generation == 0 - - override def close() {} - - override def config = { - - def getConfig(key: ConfigKeyT) = { - val builderWithModelConfig = root.getConfig(newBuilderInstance(key), key.getConfigId) - - require(builderWithModelConfig != null, "Invalid config id " + key.getConfigId ) - (key.asInstanceOf[ConfigKey[ConfigInstance]], newConfigInstance(builderWithModelConfig)) - } - - (configKeys map getConfig).toMap.asJava - } - - override def waitNextGeneration() = { - generation += 1 - - if (generation != 0) { - while (!Thread.interrupted()) - Thread.sleep(10000) - } - - generation - } - - //if waitNextGeneration has not yet been called, -1 should be returned - var generation = -1L - } - - override def getSubscriber(configKeys: java.util.Set[_ <: ConfigKey[_]]) = - new StandaloneSubscriber(configKeys.asScala.toSet.asInstanceOf[Set[ConfigKeyT]]) - - def reloadActiveSubscribers(generation: Long) { - throw new RuntimeException("unsupported") - } -} - -object StandaloneSubscriberFactory { - - private def newBuilderInstance(key: ConfigKeyT) = - builderClass(key).getDeclaredConstructor().newInstance() - - private def builderClass(key: ConfigKeyT) = { - val nestedClasses = key.getConfigClass.getClasses - nestedClasses. - filter {_.getName.equals(key.getConfigClass.getName + "$Builder")}. - head. - asInstanceOf[Class[ConfigInstance.Builder]] - } - - private def newConfigInstance(builder: ConfigBuilder) = - configClass(builder).getConstructor(builder.getClass).newInstance(builder) - - private def configClass(builder: ConfigBuilder) = - builder.getClass.getEnclosingClass.asInstanceOf[Class[ConfigInstance]] - -} diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java new file mode 100644 index 00000000000..1cd110d8106 --- /dev/null +++ b/standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java @@ -0,0 +1,67 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.yahoo.vespa.model.container.configserver.option.CloudConfigOptions; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static com.yahoo.container.standalone.CloudConfigInstallVariables.toConfigModelsPluginDir; +import static com.yahoo.container.standalone.CloudConfigInstallVariables.toConfigServer; +import static com.yahoo.container.standalone.CloudConfigInstallVariables.toConfigServers; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertThat; + +/** + * @author Ulf Lilleengen + * @author Tony Vaagenes + */ +public class CloudConfigInstallVariablesTest { + + @Test + public void test_configserver_parsing() { + CloudConfigOptions.ConfigServer[] parsed = toConfigServers("myhost.mydomain.com"); + assertThat(parsed.length, is(1)); + } + + @Test + public void port_can_be_configured() { + CloudConfigOptions.ConfigServer[] parsed = toConfigServers("myhost:123"); + int port = parsed[0].port.get(); + assertThat(port, is(123)); + } + + @Test + public void multiple_spaces_are_supported() { + CloudConfigOptions.ConfigServer[] parsed = toConfigServers("test1 test2"); + assertThat(parsed.length, is(2)); + + List hostNames = Arrays.stream(parsed).map(cs -> cs.hostName).collect(Collectors.toList()); + assertThat(hostNames, contains("test1", "test2")); + } + + @Test(expected = IllegalArgumentException.class) + public void missing_port_gives_exception() { + toConfigServer("myhost:"); + } + + @Test(expected = IllegalArgumentException.class) + public void non_numeric_port_gives_exception() { + toConfigServer("myhost:non-numeric"); + } + + @Test + public void string_arrays_are_split_on_spaces() { + String[] parsed = toConfigModelsPluginDir("/home/vespa/foo /home/vespa/bar "); + assertThat(parsed.length, is(2)); + } + + @Test + public void string_arrays_are_split_on_comma() { + String[] parsed = toConfigModelsPluginDir("/home/vespa/foo,/home/vespa/bar,"); + assertThat(parsed.length, is(2)); + } +} diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java new file mode 100644 index 00000000000..a00ffd8b985 --- /dev/null +++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java @@ -0,0 +1,61 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.yahoo.collections.Pair; +import com.yahoo.config.model.ConfigModelRepo; +import com.yahoo.config.model.producer.AbstractConfigProducerRoot; +import com.yahoo.io.IOUtils; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +/** + * Creates a local application from vespa-services fragments. + * + * @author Tony Vaagenes + * @author ollivir + */ +public class StandaloneContainer { + public static String firstContainerId(AbstractConfigProducerRoot root) { + return root.getConfigProducer("container").get().getConfigId(); + } + + interface ThrowingFunction { + U apply(T input) throws Exception; + } + + static T withContainerModel(String servicesXml, ThrowingFunction f) throws Exception { + return withTempDirectory(applicationPath -> { + writeServicesXml(applicationPath, servicesXml); + + LocalFileDb distributedFiles = new LocalFileDb(applicationPath); + VespaModel root; + Pair rc = StandaloneContainerApplication.createContainerModel( + applicationPath, distributedFiles, applicationPath.resolve("preprocesedApp").toFile(), Networking.enable, + new ConfigModelRepo()); + root = rc.getFirst(); + return f.apply(root); + }); + } + + private static T withTempDirectory(ThrowingFunction f) throws Exception { + Path directory = Files.createTempDirectory("application"); + try { + return f.apply(directory); + } finally { + IOUtils.recursiveDeleteDir(directory.toFile()); + } + } + + private static void writeServicesXml(Path applicationPath, String servicesXml) throws IOException { + Path path = applicationPath.resolve("services.xml"); + List output = Arrays.asList("", servicesXml); + Files.write(path, output, StandardCharsets.UTF_8); + } +} diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java index 8d413ade0f0..71668b595a0 100644 --- a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java +++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java @@ -1,4 +1,4 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.container.standalone; import com.google.inject.Module; @@ -25,7 +25,6 @@ import static org.junit.Assert.assertThat; /** * @author Einar M R Rosenvinge - * @since 5.22.0 */ public class StandaloneContainerActivatorTest { @@ -100,7 +99,7 @@ public class StandaloneContainerActivatorTest { private static Module newAppDirBinding(final Path applicationDir) { return binder -> binder.bind(Path.class) - .annotatedWith(StandaloneContainerApplication.applicationPathName()) + .annotatedWith(StandaloneContainerApplication.APPLICATION_PATH_NAME) .toInstance(applicationDir); } diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java new file mode 100644 index 00000000000..6d4abc84dbc --- /dev/null +++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java @@ -0,0 +1,74 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.yahoo.vespa.model.AbstractService; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Tony Vaagenes + * @author gjoranv + * @author ollivir + */ + +public class StandaloneContainerTest { + private static final String PLAIN_XML = ""; + + @Test + public void container_is_allowed_root_element() throws Exception { + StandaloneContainer.withContainerModel(PLAIN_XML, root -> null); + } + + @Test + public void services_is_allowed_root_element() throws Exception { + String servicesXml = "" + // + "" + // + ""; + + StandaloneContainer.withContainerModel(servicesXml, root -> null); + } + + @Test(expected = Exception.class) + public void multiple_container_elements_cannot_be_deployed() throws Exception { + String twoContainersXml = "" + // + "" + // + "" + // + ""; + + StandaloneContainer.withContainerModel(twoContainersXml, root -> null); + } + + @Test + public void application_preprocessor_is_run() throws Exception { + String servicesXml = "" + // + "" + // + "container-1" + // + "" + // + "" + // + ""; + + StandaloneContainer.withContainerModel(servicesXml, root -> { + assertTrue(root.getConfigProducer("container-1/standalone").isPresent()); + return null; + }); + } + + @Test + public void no_default_ports_are_enabled_when_using_http() throws Exception { + String xml = "" + // + "" + // + "" + // + "" + // + ""; + + StandaloneContainer.withContainerModel(xml, root -> { + AbstractService container = (AbstractService) root.getConfigProducer("jdisc/standalone").get(); + System.out.println("portCnt: " + container.getPortCount()); + System.out.println("numPorts: " + container.getNumPortsAllocated()); + assertEquals(1, container.getNumPortsAllocated()); + return null; + }); + } +} diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java new file mode 100644 index 00000000000..dd755e8e6dd --- /dev/null +++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java @@ -0,0 +1,52 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.standalone; + +import com.yahoo.config.ConfigInstance; +import com.yahoo.container.BundlesConfig; +import com.yahoo.container.ComponentsConfig; +import com.yahoo.container.di.config.Subscriber; +import com.yahoo.vespa.config.ConfigKey; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.yahoo.container.standalone.StandaloneContainer.withContainerModel; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.number.OrderingComparison.greaterThan; +import static org.junit.Assert.assertThat; + +/** + * @author Tony Vaagenes + * @author ollivir + */ +public class StandaloneSubscriberTest { + private static ConfigKey bundlesKey = key("bundles"); + private static ConfigKey componentsKey = key("components"); + + private static ConfigKey key(String name) { + return new ConfigKey<>(name, "container", "container"); + } + + @Test + @Ignore + public void standalone_subscriber() throws Exception { + withContainerModel("", root -> { + Set> keys = new HashSet<>(); + keys.add(bundlesKey); + keys.add(componentsKey); + Subscriber subscriber = new StandaloneSubscriberFactory(root).getSubscriber(keys); + Map, ConfigInstance> config = subscriber.config(); + assertThat(config.size(), is(2)); + + BundlesConfig bundlesConfig = (BundlesConfig) config.get(bundlesKey); + ComponentsConfig componentsConfig = (ComponentsConfig) config.get(componentsKey); + + assertThat(bundlesConfig.bundle().size(), is(0)); + assertThat(componentsConfig.components().size(), greaterThan(10)); + return null; + }); + } +} diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala deleted file mode 100644 index d4baea43ba2..00000000000 --- a/standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -import com.yahoo.container.standalone.CloudConfigInstallVariables.{toConfigModelsPluginDir, toConfigServer, toConfigServers} -import org.hamcrest.CoreMatchers.is -import org.hamcrest.Matchers.arrayContaining -import org.junit.Assert.assertThat -import org.junit.Test - -/** - * @author Ulf Lilleengen - * @author Tony Vaagenes - */ -class CloudConfigInstallVariablesTest { - - @Test - def test_configserver_parsing { - val parsed = toConfigServers("myhost.mydomain.com") - assertThat(parsed.length, is(1)) - } - - @Test - def port_can_be_configured { - val parsed = toConfigServers("myhost:123") - val port: Int = parsed(0).port.get() - assertThat(port, is(123)) - } - - @Test - def multiple_spaces_are_supported { - val parsed = toConfigServers("test1 test2") - assertThat(parsed.size, is(2)) - - val hostNames = parsed.map(_.hostName) - assertThat(hostNames, arrayContaining("test1", "test2")) - } - - @Test(expected = classOf[IllegalArgumentException]) - def missing_port_gives_exception { - toConfigServer("myhost:") - } - - @Test(expected = classOf[IllegalArgumentException]) - def non_numeric_port_gives_exception { - toConfigServer("myhost:non-numeric") - } - - @Test - def string_arrays_are_split_on_spaces { - val parsed = toConfigModelsPluginDir("/home/vespa/foo /home/vespa/bar ") - assertThat(parsed.size, is(2)) - } - - @Test - def string_arrays_are_split_on_comma { - val parsed = toConfigModelsPluginDir("/home/vespa/foo,/home/vespa/bar,") - assertThat(parsed.size, is(2)) - } -} diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala deleted file mode 100644 index 33f9a2e8594..00000000000 --- a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -import com.yahoo.config.model.producer.AbstractConfigProducerRoot -import com.yahoo.config.model.test.MockRoot -import com.yahoo.container.Container -import com.yahoo.jdisc.test.TestDriver -import scala.xml.Node -import com.yahoo.vespa.model.VespaModel -import com.yahoo.io.IOUtils -import java.nio.file.{Files, Path} -import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking - -/** - * Creates a local application from vespa-services fragments. - * - * @author tonytv - */ -object StandaloneContainer { - def firstContainerId(root: AbstractConfigProducerRoot): String = { - root.getConfigProducer("container").get().getConfigId - } - - def withStandaloneContainer[T](containerNode: Node) { - withTempDirectory { applicationDirectory => - System.setProperty(StandaloneContainerApplication.applicationLocationInstallVariable, applicationDirectory.toString) - createServicesXml(applicationDirectory, containerNode) - - val driver = TestDriver.newInjectedApplicationInstance(classOf[StandaloneContainerApplication]) - driver.close() - Container.resetInstance() - } - } - - def withContainerModel[T](containerNode: Node)(f: VespaModel => T) { - withTempDirectory { applicationPath => - createServicesXml(applicationPath, containerNode) - - val distributedFiles = new LocalFileDb(applicationPath) - val (root, container) = StandaloneContainerApplication.createContainerModel( - applicationPath, - distributedFiles, - applicationPath.resolve("preprocesedApp").toFile, - networkingOption = Networking.enable) - f(root) - } - } - - private def withTempDirectory[T](f : Path => T) : T = { - val directory = Files.createTempDirectory("application") - try { - f(directory) - } finally { - IOUtils.recursiveDeleteDir(directory.toFile) - } - } - - private def createServicesXml(applicationPath : Path, - containerNode: Node) { - - scala.xml.XML.save(applicationPath.resolve("services.xml").toFile.getAbsolutePath, - containerNode, xmlDecl = true) - } -} diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala deleted file mode 100644 index 87bef2efd95..00000000000 --- a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - - -import com.yahoo.container.standalone.StandaloneContainerTest._ -import com.yahoo.vespa.model.AbstractService -import org.junit.Assert._ -import org.junit.Test - -import scala.util.Try - - -/** - * @author tonytv - * @author gjoranv - */ - -class StandaloneContainerTest { - @Test - def container_is_allowed_root_element() { - StandaloneContainer.withContainerModel(plainXml) { root => } - } - - @Test - def services_is_allowed_root_element() { - val servicesXml = - - - - - StandaloneContainer.withContainerModel(servicesXml) { root => } - } - - @Test - def multiple_container_elements_cannot_be_deployed() { - val twoContainersXml = - - - - - - assertTrue( - Try { - StandaloneContainer.withContainerModel(twoContainersXml) { root => } - }.isFailure) - } - - @Test - def application_preprocessor_is_run() { - val servicesXml = - - - container-1 - - - - StandaloneContainer.withContainerModel(servicesXml) { - root => - assertTrue(root.getConfigProducer("container-1/standalone").isPresent) - } - } - - @Test - def no_default_ports_are_enabled_when_using_http() { - val xml = - - - - - - - StandaloneContainer.withContainerModel(xml) { root => - val container = root.getConfigProducer("jdisc/standalone").get().asInstanceOf[AbstractService] - println("portCnt: " + container.getPortCount) - println("numPorts: " + container.getNumPortsAllocated) - assertEquals(1, container.getNumPortsAllocated) - } - } - -} - -object StandaloneContainerTest { - - val plainXml = -} diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala deleted file mode 100644 index ab6f486c748..00000000000 --- a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.standalone - -import org.junit.{Ignore, Test} -import org.junit.Assert.assertThat -import org.hamcrest.CoreMatchers.is -import org.hamcrest.number.OrderingComparison.greaterThan - -import StandaloneContainer.withContainerModel -import com.yahoo.vespa.config.ConfigKey -import com.yahoo.config.ConfigInstance -import com.yahoo.container.{ComponentsConfig, BundlesConfig, di} -import scala.collection.JavaConverters._ - -/** - * @author tonytv - */ -class StandaloneSubscriberTest { - val bundlesKey = key("bundles") - val componentsKey = key("components") - - def key(name: String) = new ConfigKey(name, "container", "container").asInstanceOf[ConfigKey[ConfigInstance]] - - def box(i: Int) = java.lang.Integer.valueOf(i) - - @Test - @Ignore - def standalone_subscriber() { - withContainerModel( ) { root => - val subscriber = new StandaloneSubscriberFactory(root).getSubscriber(Set(bundlesKey, componentsKey).asJava) - val config = subscriber.config.asScala - assertThat(config.size, is(2)) - - val bundlesConfig = config(bundlesKey).asInstanceOf[BundlesConfig] - val componentsConfig = config(componentsKey).asInstanceOf[ComponentsConfig] - - assertThat(bundlesConfig.bundle().size(), is(0)) - assertThat(box(componentsConfig.components().size()), greaterThan(box(10))) - } - } -} -- cgit v1.2.3