Copy/port some stuff from STS codebase

- yaml schema abstraction
- manifest yaml schema
- manifest yaml schema tests
- reconciler abstractions
This commit is contained in:
Kris De Volder
2016-09-23 11:44:50 -07:00
parent a2390daec3
commit 2b0927ed9b
55 changed files with 1947 additions and 13 deletions

View File

@@ -0,0 +1,5 @@
package org.springframework.ide.vscode.commons.reconcile;
public interface IDocument {
}

View File

@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2014-2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.reconcile;
public interface IProblemCollector {
void beginCollecting();
void endCollecting();
void accept(ReconcileProblem problem);
/**
* Problem collector that simply ignores/discards anything passed to it.
*/
IProblemCollector NULL = new IProblemCollector() {
public void beginCollecting() {
}
public void endCollecting() {
}
public void accept(ReconcileProblem problem) {
}
};
}

View File

@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.reconcile;
import org.springframework.ide.vscode.util.TextDocument;
public interface IReconcileEngine {
public void reconcile(IDocument doc, IProblemCollector problemCollector);
}

View File

@@ -0,0 +1,12 @@
package org.springframework.ide.vscode.commons.reconcile;
/**
* @author Kris De Volder
*/
public enum ProblemSeverity {
IGNORE,
WARNING,
ERROR;
}

View File

@@ -0,0 +1,17 @@
package org.springframework.ide.vscode.commons.reconcile;
/**
* Besides the methods below, the only hard requirement for a 'problem type' is
* that it is a unique object that is not 'equals' to any other object.
* <p>
* It is probably nice if you implement a good toString however.
* <p>
* A good way to implement a discrete set of problemType objects is as an enum
* that implements this interace.
*
* @author Kris De Volder
*/
public interface ProblemType {
ProblemSeverity getDefaultSeverity();
String toString();
}

View File

@@ -0,0 +1,14 @@
package org.springframework.ide.vscode.commons.reconcile;
/**
* Minamal interface that objects representing a reconciler problem must
* implement.
*
* @author Kris De Volder
*/
public interface ReconcileProblem {
ProblemType getType();
String getMessage();
int getOffset();
int getLength();
}

View File

@@ -7,5 +7,11 @@ public class Assert {
throw new IllegalStateException(msg);
}
}
public static void isLegal(boolean b) {
if (!b) {
throw new IllegalStateException();
}
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.ide.vscode.util;
public class StringUtil {
public static boolean hasText(String name) {
return name!=null && !name.trim().equals("");
}
}

View File

@@ -1,9 +1,11 @@
package org.springframework.ide.vscode.util;
import org.springframework.ide.vscode.commons.reconcile.IDocument;
import io.typefox.lsapi.Range;
import io.typefox.lsapi.TextDocumentContentChangeEvent;
public class TextDocument {
public class TextDocument implements IDocument {
private final String uri;
private String text = "";

View File

@@ -34,8 +34,7 @@ import io.typefox.lsapi.services.LanguageServer;
public class LanguageServerHarness {
//Warning this 'harness' is not very good yet. It just implements bare minimum to
// be able to test the MyLanguageServer example.
//Warning this 'harness' is incomplete. Growing it as needed.
private Random random = new Random();

View File

@@ -12,11 +12,13 @@
<modules>
<module>language-server-commons</module>
<module>language-server-test-harness</module>
<module>yaml-commons</module>
</modules>
<properties>
<junit-version>4.11</junit-version>
<assertj-version>3.5.2</assertj-version>
<slf4j-version>1.7.21</slf4j-version>
</properties>
<build>
@@ -35,6 +37,11 @@
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>yaml-commons</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>yaml-commons</artifactId>
<name>yaml-commons</name>
<description>Shared utilities for working with yaml</description>
<parent>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.17</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
public class BasicYValueHint implements YValueHint {
private final String value;
private String label;
public BasicYValueHint(String value, String label) {
this.value = value;
this.label = label;
}
public BasicYValueHint(String value) {
this.value = value;
this.label = value;
}
/* (non-Javadoc)
* @see org.springframework.ide.eclipse.cloudfoundry.manifest.editor.YValueHint#getValue()
*/
@Override
public String getValue() {
return value;
}
/* (non-Javadoc)
* @see org.springframework.ide.eclipse.cloudfoundry.manifest.editor.YValueHint#getLabel()
*/
@Override
public String getLabel() {
return label;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((label == null) ? 0 : label.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BasicYValueHint other = (BasicYValueHint) obj;
if (label == null) {
if (other.label != null)
return false;
} else if (!label.equals(other.label))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
@Override
public String toString() {
return "BasicYValueHint [value=" + value + ", label=" + label + "]";
}
}

View File

@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
/**
* Marker interface for objects that carry 'type information'.
* <p>
* It may seem odd that this interface has no actual methods. This
* is because the methods for interpreting the types are defined
* by an accompanying {@link YTypeUtil}.
* <p>
* The main reason why it works this way is to allow for 'YType' objects
* themselves to be implemented as dumb data objects while making YTypeUtil
* implementations define how to interpret these objects using context
* information (e.g. types resolved from a project's classpath).
*
* @author Kris De Volder
*/
public interface YType {
}

View File

@@ -0,0 +1,364 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Provider;
import org.springframework.ide.vscode.yaml.util.Description;
import org.springframework.ide.vscode.yaml.util.DescriptionProviders;
import org.springframework.ide.vscode.yaml.util.EnumValueParser;
import org.springframework.ide.vscode.yaml.util.ValueParser;
/**
* Static utility method for creating YType objects representing either
* 'array-like', 'map-like' or 'object-like' types which can be used
* to build up a 'Yaml Schema'.
*
* @author Kris De Volder
*/
public class YTypeFactory {
public YType yseq(YType el) {
return new YSeqType(el);
}
public YType ymap(YType key, YType val) {
return new YMapType(key, val);
}
public YBeanType ybean(String name, YTypedProperty... properties) {
return new YBeanType(name, properties);
}
/**
* YTypeUtil instances capable of 'interpreting' the YType objects created by this
* YTypeFactory
*/
public final YTypeUtil TYPE_UTIL = new YTypeUtil() {
@Override
public boolean isSequencable(YType type) {
return ((AbstractType)type).isSequenceable();
}
@Override
public boolean isMap(YType type) {
return ((AbstractType)type).isMap();
}
@Override
public boolean isAtomic(YType type) {
return ((AbstractType)type).isAtomic();
}
@Override
public Map<String, YTypedProperty> getPropertiesMap(YType type) {
return ((AbstractType)type).getPropertiesMap();
}
@Override
public List<YTypedProperty> getProperties(YType type) {
return ((AbstractType)type).getProperties();
}
@Override
public YValueHint[] getHintValues(YType type) {
return ((AbstractType)type).getHintValues();
}
@Override
public YType getDomainType(YType type) {
return ((AbstractType)type).getDomainType();
}
@Override
public String niceTypeName(YType type) {
return type.toString();
}
@Override
public YType getKeyType(YType type) {
return ((AbstractType)type).getKeyType();
}
@Override
public boolean isBean(YType type) {
return ((AbstractType)type).isBean();
}
@Override
public ValueParser getValueParser(YType type) {
return ((AbstractType)type).getParser();
}
};
/////////////////////////////////////////////////////////////////////////////////////
/**
* Provides default implementations for all YType methods.
*/
public static abstract class AbstractType implements YType {
private ValueParser parser;
private List<YTypedProperty> propertyList = new ArrayList<>();
private final List<YValueHint> hints = new ArrayList<>();
private Map<String, YTypedProperty> cachedPropertyMap;
private Provider<Collection<YValueHint>> hintProvider;
public boolean isSequenceable() {
return false;
}
public boolean isBean() {
return false;
}
public YType getKeyType() {
return null;
}
public YType getDomainType() {
return null;
}
public void addHintProvider(Provider<Collection<YValueHint>> hintProvider) {
this.hintProvider = hintProvider;
}
public YValueHint[] getHintValues() {
Collection<YValueHint> providerHints = hintProvider != null ? hintProvider.get() : null;
if (providerHints == null || providerHints.isEmpty()) {
return hints.toArray(new YValueHint[hints.size()]);
} else {
// Only merge if there are provider hints to merge
Set<YValueHint> mergedHints = new LinkedHashSet<>();
// Add type hints first
for (YValueHint val : hints) {
mergedHints.add(val);
}
// merge the provider hints
for (YValueHint val : providerHints) {
mergedHints.add(val);
}
return mergedHints.toArray(new YValueHint[mergedHints.size()]);
}
}
public final List<YTypedProperty> getProperties() {
return Collections.unmodifiableList(propertyList);
}
public final Map<String, YTypedProperty> getPropertiesMap() {
if (cachedPropertyMap==null) {
cachedPropertyMap = new LinkedHashMap<>();
for (YTypedProperty p : propertyList) {
cachedPropertyMap.put(p.getName(), p);
}
}
return Collections.unmodifiableMap(cachedPropertyMap);
}
public boolean isAtomic() {
return false;
}
public boolean isMap() {
return false;
}
public abstract String toString(); // force each sublcass to implement a (nice) toString method.
public void addProperty(YTypedProperty p) {
cachedPropertyMap = null;
propertyList.add(p);
}
public void addProperty(String name, YType type, Provider<Description> description) {
YTypedPropertyImpl prop;
addProperty(prop = new YTypedPropertyImpl(name, type));
prop.setDescriptionProvider(description);
}
public void addProperty(String name, YType type) {
addProperty(new YTypedPropertyImpl(name, type));
}
public void addHints(String... strings) {
if (strings != null) {
for (String value : strings) {
BasicYValueHint hint = new BasicYValueHint(value);
if (!hints.contains(hint)) {
hints.add(hint);
}
}
}
}
public void parseWith(ValueParser parser) {
this.parser = parser;
}
public ValueParser getParser() {
return parser;
}
}
public static class YMapType extends AbstractType {
private final YType key;
private final YType val;
private YMapType(YType key, YType val) {
this.key = key;
this.val = val;
}
@Override
public String toString() {
return "Map<"+key.toString()+", "+val.toString()+">";
}
@Override
public boolean isMap() {
return true;
}
@Override
public YType getKeyType() {
return key;
}
@Override
public YType getDomainType() {
return val;
}
}
public static class YSeqType extends AbstractType {
private YType el;
private YSeqType(YType el) {
this.el = el;
}
@Override
public String toString() {
return el.toString()+"[]";
}
@Override
public boolean isSequenceable() {
return true;
}
@Override
public YType getDomainType() {
return el;
}
}
public static class YBeanType extends AbstractType {
private final String name;
public YBeanType(String name, YTypedProperty[] properties) {
this.name = name;
for (YTypedProperty p : properties) {
addProperty(p);
}
}
@Override
public String toString() {
return name;
}
public boolean isBean() {
return true;
}
}
public static class YAtomicType extends AbstractType {
private final String name;
private YAtomicType(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
@Override
public boolean isAtomic() {
return true;
}
}
public static class YTypedPropertyImpl implements YTypedProperty {
final private String name;
final private YType type;
private Provider<Description> descriptionProvider = DescriptionProviders.NO_DESCRIPTION;
private YTypedPropertyImpl(String name, YType type) {
this.name = name;
this.type = type;
}
@Override
public String getName() {
return this.name;
}
@Override
public YType getType() {
return this.type;
}
@Override
public String toString() {
return name + ":" + type;
}
@Override
public Description getDescription() {
return descriptionProvider.get();
}
public void setDescriptionProvider(Provider<Description> descriptionProvider) {
this.descriptionProvider = descriptionProvider;
}
}
public YAtomicType yatomic(String name) {
return new YAtomicType(name);
}
public YTypedPropertyImpl yprop(String name, YType type) {
return new YTypedPropertyImpl(name, type);
}
public YAtomicType yenum(String name, String... values) {
YAtomicType t = yatomic(name);
t.addHints(values);
t.parseWith(new EnumValueParser(name, values));
return t;
}
}

View File

@@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
import java.util.List;
import java.util.Map;
import org.springframework.ide.vscode.yaml.util.ValueParser;
/**
* An implementation of YTypeUtil provides implementations of various
* methods operating on YTypes and interpreting them in some context
* (e.g. the meaning of YType objects may depend on types resolved
* from the current project's classpath).
*
* @author Kris De Volder
*/
public interface YTypeUtil {
boolean isAtomic(YType type);
boolean isMap(YType type);
boolean isSequencable(YType type);
boolean isBean(YType type);
YType getDomainType(YType type);
YValueHint[] getHintValues(YType yType);
String niceTypeName(YType type);
YType getKeyType(YType type);
ValueParser getValueParser(YType type);
//TODO: only one of these two should be enough?
List<YTypedProperty> getProperties(YType type);
Map<String, YTypedProperty> getPropertiesMap(YType yType);
}

View File

@@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
import org.springframework.ide.vscode.yaml.util.Description;
/**
* @author Kris De Volder
*/
public interface YTypedProperty {
String getName();
YType getType();
Description getDescription();
}

View File

@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
public interface YValueHint {
String getValue();
String getLabel();
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.schema;
/**
* A 'schema' provides a toplevel type, which dictates the valid structure of a
* YamlDocument and a {@link YTypeUtil} which provides the means to 'interpret'
* the types.
*
* @author Kris De Volder
*/
public interface YamlSchema {
YType getTopLevelType();
YTypeUtil getTypeUtil();
}

View File

@@ -0,0 +1,31 @@
package org.springframework.ide.vscode.yaml.util;
public abstract class Description {
public abstract void renderAsText(StringBuilder buf);
public void renderAsHtml(StringBuilder buf) {
throw new UnsupportedOperationException("Rendering as html not supported");
}
public static Description text(String text) {
return new Description() {
@Override
public void renderAsText(StringBuilder buf) {
buf.append(text);
}
};
}
public static Description italic(Description d) {
//Not really supported, we just ignore italic and display as is
return d;
}
public String toText() {
StringBuilder buf = new StringBuilder();
renderAsText(buf);
return buf.toString();
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.util;
import java.io.InputStream;
import javax.inject.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.springframework.ide.vscode.yaml.util.Description.*;
/**
* Static methods and convenience constants for creating some 'description providers'.
*
* @author Kris De Volder
*/
public class DescriptionProviders {
final static Logger logger = LoggerFactory.getLogger(DescriptionProviders.class);
public static final Provider<Description> NO_DESCRIPTION = () -> italic(text("no description"));
public static Provider<Description> snippet(final Description snippet) {
return new Provider<Description>() {
@Override
public String toString() {
return snippet.toString();
}
@Override
public Description get() {
return snippet;
}
};
}
public static Provider<Description> fromClasspath(final Class<?> klass, final String resourcePath) {
return new Provider<Description>() {
@Override
public String toString() {
return "DescriptionFromClassPth(class="+klass.getSimpleName()+", "+resourcePath+")";
}
@Override
public Description get() {
try {
InputStream stream = klass.getResourceAsStream(resourcePath);
if (stream!=null) {
return Description.text(IOUtil.toString(stream));
}
} catch (Exception e) {
logger.error("Error", e);;
}
return NO_DESCRIPTION.get();
}
};
}
}

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2014-2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.util;
import java.util.Collection;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
/**
* Parser for checking a 'Enum' style values.
*
* @author Kris De Volder
*/
public class EnumValueParser implements ValueParser {
private String typeName;
private Set<String> values;
public EnumValueParser(String typeName, String... values) {
this(typeName, ImmutableSet.copyOf(values));
}
public EnumValueParser(String typeName, Collection<String> values) {
this.typeName = typeName;
this.values = ImmutableSet.copyOf(values);
}
public Object parse(String str) {
if (values.contains(str)) {
return str;
} else {
throw new IllegalArgumentException("'"+str+"' is not valid for Enum '"+typeName+"'");
}
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* Copyright (c) 2013 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.util;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class IOUtil {
/**
* Copy data from an inputstream into a file until end of the inputstream
* is reached.
* <p>
* The input stream is closed automatically.
*/
public static void pipe(InputStream data, File target) throws IOException {
target.getParentFile().mkdirs(); //try to create dirs for parent if they don't exist.
OutputStream out = new BufferedOutputStream(new FileOutputStream(target));
try {
pipe(data, out);
} finally {
out.close();
}
}
/**
* Copy input stream to output stream until end of the inputstream is reached.
* The intpustream is closed automatically, but the output stream is not.
*/
public static void pipe(InputStream input, OutputStream output) throws IOException {
try {
byte[] buf = new byte[1024*4];
int n = input.read(buf);
while (n >= 0) {
output.write(buf, 0, n);
n = input.read(buf);
}
output.flush();
} finally {
input.close();
}
}
public static String toString(InputStream input) throws Exception {
return toString(input, "UTF8");
}
private static String toString(InputStream input, String encoding) throws Exception {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
pipe(input, buf);
return buf.toString(encoding);
}
/**
* Sick and tired of writing try-catch around close calls... If something can't close, it usually means it
* was already closed, no longer exists etc. This method catches and ignores the exceptions.
*/
public static void close(Closeable closeable) {
try {
closeable.close();
} catch (IOException e) {
//ignore
}
}
public static byte[] toBytes(InputStream stream) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
pipe(stream, bytes);
return bytes.toByteArray();
}
}

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2015-2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.yaml.util;
/**
* A ValueParser provides the means to Strings into some kind of
* value.
*
* @author Kris De Volder
*/
public interface ValueParser {
/**
* Parse the string and return its parsed representation.
* May either return null, or throw an {@link IllegalArgumentException} to indicate
* that the String is not the format this parser expects.
*/
Object parse(String str);
}

View File

@@ -38,15 +38,9 @@
</dependency>
<!-- Yaml -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.17</version>
</dependency>
<!-- Guava collections -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>yaml-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Test harness -->
<dependency>

View File

@@ -0,0 +1,80 @@
///*******************************************************************************
// * Copyright (c) 2016 Pivotal, Inc.
// * All rights reserved. This program and the accompanying materials
// * are made available under the terms of the Eclipse Public License v1.0
// * which accompanies this distribution, and is available at
// * http://www.eclipse.org/legal/epl-v10.html
// *
// * Contributors:
// * Pivotal, Inc. - initial API and implementation
// *******************************************************************************/
//package org.springframework.ide.eclipse.cloudfoundry.manifest.editor;
//
//import java.util.Collection;
//
//import javax.inject.Provider;
//
//import org.springframework.ide.eclipse.editor.support.yaml.schema.YValueHint;
//import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
//
//public class ManifestEditorActivator {
//
// // The plug-in ID
// public static final String PLUGIN_ID = "org.springframework.ide.eclipse.cloudfoundry.manifest.editor"; //$NON-NLS-1$
//
// // The shared instance
// private static ManifestEditorActivator plugin;
//
// /**
// * The constructor
// */
// public ManifestEditorActivator() {
// }
//
// /*
// * (non-Javadoc)
// * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
// */
// public void start(BundleContext context) throws Exception {
// super.start(context);
// plugin = this;
// }
//
// /*
// * (non-Javadoc)
// * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
// */
// public void stop(BundleContext context) throws Exception {
// plugin = null;
// super.stop(context);
// }
//
// /**
// * Returns the shared instance
// *
// * @return the shared instance
// */
// public static ManifestEditorActivator getDefault() {
// return plugin;
// }
//
// public static void log(Throwable e) {
// getDefault().getLog().log(ExceptionUtil.status(e));
// }
//
//
// /*
// *
// * "Framework" to contribute value hints into manifest editor
// */
//
// private Provider<Collection<YValueHint>> buildpackProvider;
//
// public void setBuildpackProvider(Provider<Collection<YValueHint>> buildpackProvider) {
// this.buildpackProvider = buildpackProvider;
// }
//
// public Provider<Collection<YValueHint>> getBuildpackProvider() {
// return this.buildpackProvider;
// }
//}

View File

@@ -0,0 +1,54 @@
///*******************************************************************************
// * Copyright (c) 2015 Pivotal, Inc.
// * All rights reserved. This program and the accompanying materials
// * are made available under the terms of the Eclipse Public License v1.0
// * which accompanies this distribution, and is available at
// * http://www.eclipse.org/legal/epl-v10.html
// *
// * Contributors:
// * Pivotal, Inc. - initial API and implementation
// *******************************************************************************/
//package org.springframework.ide.eclipse.cloudfoundry.manifest.editor;
//
//import org.dadacoalition.yedit.editor.YEditSourceViewerConfiguration;
//import org.springframework.ide.eclipse.editor.support.util.ShellProviders;
//import org.springframework.ide.eclipse.editor.support.yaml.AbstractYamlEditor;
//
//
//public class ManifestYamlEditor extends AbstractYamlEditor {
//
// @Override
// protected YEditSourceViewerConfiguration createSourceViewerConfiguration() {
// return new ManifestYamlSourceViewerConfiguration(ShellProviders.from(this));
// }
//
// @Override
// protected void initializeEditor() {
// super.initializeEditor();
//// SpringPropertiesEditorPlugin.getIndexManager().addListener(this);
//// SpringPropertiesEditorPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
// }
//
//// @Override
//// public void changed(SpringPropertiesIndexManager info) {
//// if (sourceViewerConf!=null) {
//// sourceViewerConf.forceReconcile();
//// }
//// }
//
// @Override
// public void dispose() {
// super.dispose();
//// SpringPropertiesEditorPlugin.getIndexManager().removeListener(this);
//// SpringPropertiesEditorPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
// }
//
//// @Override
//// public void propertyChange(PropertyChangeEvent event) {
//// if (event.getProperty().startsWith(ProblemSeverityPreferencesUtil.PREFERENCE_PREFIX)) {
//// if (sourceViewerConf!=null) {
//// sourceViewerConf.forceReconcile();
//// }
//// }
//// }
//}

View File

@@ -0,0 +1,74 @@
///*******************************************************************************
// * Copyright (c) 2016 Pivotal, Inc.
// * All rights reserved. This program and the accompanying materials
// * are made available under the terms of the Eclipse Public License v1.0
// * which accompanies this distribution, and is available at
// * http://www.eclipse.org/legal/epl-v10.html
// *
// * Contributors:
// * Pivotal, Inc. - initial API and implementation
// *******************************************************************************/
//package org.springframework.ide.eclipse.cloudfoundry.manifest.editor;
//
//import javax.inject.Provider;
//
//import org.eclipse.jface.dialogs.IDialogSettings;
//import org.eclipse.jface.preference.IPreferenceStore;
//import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
//import org.eclipse.jface.text.source.ISourceViewer;
//import org.eclipse.swt.widgets.Shell;
//import org.springframework.ide.eclipse.editor.support.reconcile.IReconcileEngine;
//import org.springframework.ide.eclipse.editor.support.reconcile.ReconcileStrategy;
//import org.springframework.ide.eclipse.editor.support.yaml.AbstractYamlSourceViewerConfiguration;
//import org.springframework.ide.eclipse.editor.support.yaml.YamlAssistContextProvider;
//import org.springframework.ide.eclipse.editor.support.yaml.completions.SchemaBasedYamlAssistContextProvider;
//import org.springframework.ide.eclipse.editor.support.yaml.reconcile.YamlSchemaBasedReconcileEngine;
//import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureProvider;
//
///**
// * @author Kris De Volder
// */
//public class ManifestYamlSourceViewerConfiguration extends AbstractYamlSourceViewerConfiguration {
//
// private ManifestYmlSchema schema = new ManifestYmlSchema(ManifestEditorActivator.getDefault().getBuildpackProvider());
// private YamlAssistContextProvider assistContextProvider = new SchemaBasedYamlAssistContextProvider(schema);
//
// public ManifestYamlSourceViewerConfiguration(Provider<Shell> shellProvider) {
// super(shellProvider);
// }
//
// @Override
// protected YamlAssistContextProvider getAssistContextProvider() {
// return assistContextProvider;
// }
//
// @Override
// protected YamlStructureProvider getStructureProvider() {
// return YamlStructureProvider.DEFAULT;
// }
//
// @Override
// protected IDialogSettings getPluginDialogSettings() {
// return ManifestEditorActivator.getDefault().getDialogSettings();
// }
//
// @Override
// protected IReconcilingStrategy createReconcilerStrategy(ISourceViewer viewer) {
// IReconcileEngine engine = createReconcileEngine();
// return new ReconcileStrategy(viewer, engine);
// }
//
// private IReconcileEngine createReconcileEngine() {
// return new YamlSchemaBasedReconcileEngine(getAstProvider(), schema);
// }
//
// @Override
// protected IPreferenceStore getPreferencesStore() {
// return ManifestEditorActivator.getDefault().getPreferenceStore();
// }
//
// @Override
// protected String getPluginId() {
// return ManifestEditorActivator.PLUGIN_ID;
// }
//}

View File

@@ -0,0 +1,126 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.cloudfoundry.manifest.editor;
import java.util.Collection;
import java.util.Set;
import javax.inject.Provider;
import org.springframework.ide.vscode.yaml.schema.YType;
import org.springframework.ide.vscode.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.yaml.schema.YTypeFactory.YAtomicType;
import org.springframework.ide.vscode.yaml.schema.YTypeFactory.YBeanType;
import org.springframework.ide.vscode.yaml.schema.YTypeFactory.YTypedPropertyImpl;
import org.springframework.ide.vscode.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.yaml.schema.YValueHint;
import org.springframework.ide.vscode.yaml.schema.YamlSchema;
import org.springframework.ide.vscode.yaml.util.Description;
import org.springframework.ide.vscode.yaml.util.DescriptionProviders;
import com.google.common.collect.ImmutableSet;
/**
* @author Kris De Volder
*/
public class ManifestYmlSchema implements YamlSchema {
private final YBeanType TOPLEVEL_TYPE;
private final YTypeUtil TYPE_UTIL;
private final Provider<Collection<YValueHint>> buildpackProvider;
private static final Set<String> TOPLEVEL_EXCLUDED = ImmutableSet.of(
"name", "host", "hosts"
);
public ManifestYmlSchema(Provider<Collection<YValueHint>> buildpackProvider) {
this.buildpackProvider = buildpackProvider;
YTypeFactory f = new YTypeFactory();
TYPE_UTIL = f.TYPE_UTIL;
// define schema types
TOPLEVEL_TYPE = f.ybean("manifest.yml schema");
YBeanType application = f.ybean("Application");
YAtomicType t_path = f.yatomic("Path");
YAtomicType t_buildpack = f.yatomic("Buildpack");
t_buildpack.addHintProvider(this.buildpackProvider);
YAtomicType t_boolean = f.yenum("boolean", "true", "false");
YType t_string = f.yatomic("String");
YType t_strings = f.yseq(t_string);
YAtomicType t_memory = f.yatomic("Memory");
t_memory.addHints("256M", "512M", "1024M");
t_memory.parseWith(ManifestYmlValueParsers.MEMORY);
YAtomicType t_strictly_pos_integer = f.yatomic("Strictly Positive Integer");
t_strictly_pos_integer.parseWith(ManifestYmlValueParsers.integerAtLeast(1));
YAtomicType t_pos_integer = f.yatomic("Positive Integer");
t_pos_integer.parseWith(ManifestYmlValueParsers.POS_INTEGER);
YType t_env = f.ymap(t_string, t_string);
// define schema structure...
TOPLEVEL_TYPE.addProperty("applications", f.yseq(application));
TOPLEVEL_TYPE.addProperty("inherit", t_string, descriptionFor("inherit"));
YTypedPropertyImpl[] props = {
f.yprop("buildpack", t_buildpack),
f.yprop("command", t_string),
f.yprop("disk_quota", t_memory),
f.yprop("domain", t_string),
f.yprop("domains", t_strings),
f.yprop("env", t_env),
f.yprop("host", t_string),
f.yprop("hosts", t_strings),
f.yprop("instances", t_strictly_pos_integer),
f.yprop("memory", t_memory),
f.yprop("name", t_string),
f.yprop("no-hostname", t_boolean),
f.yprop("no-route", t_boolean),
f.yprop("path", t_path),
f.yprop("random-route", t_boolean),
f.yprop("services", t_strings),
f.yprop("stack", t_string),
f.yprop("timeout", t_pos_integer)
};
for (YTypedPropertyImpl prop : props) {
prop.setDescriptionProvider(descriptionFor(prop));
if (!TOPLEVEL_EXCLUDED.contains(prop.getName())) {
TOPLEVEL_TYPE.addProperty(prop);
}
application.addProperty(prop);
}
}
private Provider<Description> descriptionFor(String propName) {
return DescriptionProviders.fromClasspath(this.getClass(), "/description-by-prop-name/"+propName+".html");
}
private Provider<Description> descriptionFor(YTypedPropertyImpl prop) {
return descriptionFor(prop.getName());
}
@Override
public YBeanType getTopLevelType() {
return TOPLEVEL_TYPE;
}
@Override
public YTypeUtil getTypeUtil() {
return TYPE_UTIL;
}
}

View File

@@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.cloudfoundry.manifest.editor;
import java.util.Set;
import org.springframework.ide.vscode.util.Assert;
import org.springframework.ide.vscode.yaml.util.ValueParser;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
/**
* Methods and constants to create/get parsers for some atomic types
* used in manifest yml schema.
*
* @author Kris De Volder
*/
public class ManifestYmlValueParsers {
public static final ValueParser POS_INTEGER = integerRange(0, null);
public static final ValueParser MEMORY = new ValueParser() {
private final ImmutableSet<String> GIGABYTE = ImmutableSet.of("G", "GB");
private final ImmutableSet<String> MEGABYTE = ImmutableSet.of("M", "MB");
private final Set<String> UNITS = Sets.union(GIGABYTE, MEGABYTE);
@Override
public Object parse(String str) {
str = str.trim();
String unit = getUnit(str.toUpperCase());
if (unit==null) {
throw new NumberFormatException(
"'"+str+"' doesn't end with a valid unit of memory ('M', 'MB', 'G' or 'GB')"
);
}
str = str.substring(0, str.length()-unit.length());
int unitSize = GIGABYTE.contains(unit)?1024:1;
int value = Integer.parseInt(str);
if (value<0) {
throw new NumberFormatException("Negative value is not allowed");
}
return value * unitSize;
}
private String getUnit(String str) {
for (String u : UNITS) {
if (str.endsWith(u)) {
return u;
}
}
return null;
}
};
public static ValueParser integerAtLeast(final Integer lowerBound) {
return integerRange(lowerBound, null);
}
public static ValueParser integerRange(final Integer lowerBound, final Integer upperBound) {
Assert.isLegal(lowerBound==null || upperBound==null || lowerBound <= upperBound);
return new ValueParser() {
@Override
public Object parse(String str) {
int value = Integer.parseInt(str);
if (lowerBound!=null && value<lowerBound) {
if (lowerBound==0) {
throw new NumberFormatException("Value must be positive");
} else {
throw new NumberFormatException("Value must be at least "+lowerBound);
}
}
if (upperBound!=null && value>upperBound) {
throw new NumberFormatException("Value must be at most "+upperBound);
}
return value;
}
};
}
}

View File

@@ -37,7 +37,6 @@ public class YamlLanguageServer extends SimpleLanguageServer {
SimpleTextDocumentService documents = getTextDocumentService();
// SimpleWorkspaceService workspace = getWorkspaceService();
documents.onDidChangeContent(params -> {
System.out.println("Document changed: "+params);
TextDocument doc = params.getDocument();
validateDocument(documents, doc);
});

View File

@@ -0,0 +1,11 @@
<p>If your application requires a custom buildpack, you can use the <code>buildpack</code> attribute to specify its URL or name:</p>
<pre>
---
...
buildpack: buildpack_URL
</pre>
<p class="note"><strong>Note</strong>: The <code>cf buildpacks</code> command lists the buildpacks that you can refer to by name in a manifest or a command line option.</p>
<p>The command line option that overrides this attribute is <code>-b</code>.</p>

View File

@@ -0,0 +1,34 @@
<p>Some languages and frameworks require that you provide a custom command to start an application. Refer to the <a href="/buildpacks/">buildpack</a> documentation to determine if you need to provide a custom start command.</p>
<p>You can provide the custom start command in your application manifest or on the command line.</p>
<p>To specify the custom start command in your application manifest, add it in the <code>command: START-COMMAND</code> format as the following example shows:</p>
<pre>
---
...
command: bundle exec rake VERBOSE=true
</pre>
<p>On the command line, use the <code>-c</code> option to specify the custom start command as the following example shows:</p>
<pre class="terminal">
$ cf push my-app -c "bundle exec rake VERBOSE=true"
</pre>
<p class="note"><strong>Note</strong>: The <code>-c</code> option with a value of &lsquo;null&rsquo; forces <code>cf push</code> to use the buildpack start command. See <a href="./app-startup.html">About Starting Applications</a> for more information.</p>
<p>If you override the start command for a Buildpack application, Linux uses
<code>bash -c YOUR-COMMAND</code> to invoke your application.
If you override the start command for a Docker application, Linux uses <code>sh -c YOUR-COMMAND</code> to invoke your application.
Because of this, if you override a start command, you should prefix <code>exec</code> to the final command in your custom composite start command.</p>
<p><code>exec</code> causes the last command to become the root process of your application. The <a href="./prepare-to-deploy.html#moving-apps">Cloud Foundry Updates and Your Application</a> section of the <em>Considerations for Designing and Running an Application in the Cloud</em> topic explains why your application should handle a <code>termination signal</code> during Cloud Foundry updates.
Without an <code>exec</code> statement, the parent process remains as the implied bash process, and does not propagate signals to your application process.</p>
<p>For example, both of the following composite start commands run database migrations when the first instance of the app starts, then start the app to serve requests, but they behave differently on graceful shutdown. </p>
<ul>
<li><p><code>bin/rake cf:on_first_instance db:migrate &amp;&amp; bin/rails server -p $PORT -e $RAILS_ENV</code>: The process tree is <code>bash -&gt; ruby</code>, so on graceful shutdown only the <code>bash</code> process receives the TERM signal, and not the <code>ruby</code> process.</p></li>
<li><p><code>bin/rake cf:on_first_instance db:migrate &amp;&amp; exec bin/rails server -p $PORT -e $RAILS_ENV</code>: Because of the <code>exec</code> prefix on the final command, the <code>ruby</code> process invoked by <code>rails</code> takes over the <code>bash</code> process managing the execution of the composite command. The process tree is only <code>ruby</code>, so the ruby web server receives the TERM signal can shutdown gracefully for 10 seconds.</p></li>
</ul>

View File

@@ -0,0 +1,9 @@
<p>Use the <code>disk_quota</code> attribute to allocate the disk space for your app instance. This attribute requires a unit of measurement: <code>M</code>, <code>MB</code>, <code>G</code>, or <code>GB</code>, in upper case or lower case.</p>
<pre>
---
...
disk_quota: 1024M
</pre>
<p>The command line option that overrides this attribute is <code>-k</code>.</p>

View File

@@ -0,0 +1,27 @@
<p>Every <code>cf push</code> deploys applications to one particular Cloud Foundry instance.
Every Cloud Foundry instance may have a shared domain set by an admin.
Unless you specify a domain, Cloud Foundry incorporates that shared domain in the route to your application.</p>
<p>You can use the <code>domain</code> attribute when you want your application to be served from a domain other than the default shared domain.</p>
<pre>
---
...
domain: unique-example.com
</pre>
<p>The command line option that overrides this attribute is <code>-d</code>.</p>
<h3><a id='domains'></a>The domains attribute</h3>
<p>Use the <code>domains</code> attribute to provide multiple domains. If you define both <code>domain</code> and <code>domains</code> attributes, Cloud Foundry creates routes for domains defined in both of these fields.</p>
<pre>
---
...
domains:
- domain-example1.com
- domain-example2.org
</pre>
<p>The command line option that overrides this attribute is <code>-d</code>.</p>

View File

@@ -0,0 +1,10 @@
<p>Use the <code>domains</code> attribute to provide multiple domains. If you define both <code>domain</code> and <code>domains</code> attributes, Cloud Foundry creates routes for domains defined in both of these fields.</p>
<pre>---
...
domains:
- domain-example1.com
- domain-example2.org
</pre>
<p>The command line option that overrides this attribute is <code>-d</code>.</p>

View File

@@ -0,0 +1,28 @@
<p>The <code>env</code> block consists of a heading, then one or more environment variable/value pairs.</p>
<p>For example:</p>
<pre>
---
...
env:
RAILS_ENV: production
RACK_ENV: production
</pre>
<p><code>cf push</code> deploys the application to a container on the server. The variables belong to the container environment.</p>
<p>While the application is running, Cloud Foundry allows you to operate on environment variables.</p>
<ul>
<li>View all variables: <code>cf env my-app</code></li>
<li>Set an individual variable: <code>cf set-env my-app my-variable_name my-variable_value</code></li>
<li>Unset an individual variable: <code>cf unset-env my-app my-variable_name my-variable_value</code></li>
</ul>
<p>Environment variables interact with manifests in the following ways:</p>
<ul>
<li><p>When you deploy an application for the first time, Cloud Foundry reads the variables described in the environment block of the manifest, and adds them to the environment of the container where the application is deployed.</p></li>
<li><p>When you stop and then restart an application, its environment variables persist.</p></li>
</ul>

View File

@@ -0,0 +1,9 @@
<p>Use the <code>host</code> attribute to provide a hostname, or subdomain, in the form of a string. This segment of a route helps to ensure that the route is unique. If you do not provide a hostname, the URL for the app takes the form of <code>APP-NAME.DOMAIN</code>.</p>
<pre>
---
...
host: my-app
</pre>
<p>The command line option that overrides this attribute is <code>-n</code>.</p>

View File

@@ -0,0 +1,11 @@
<p>Use the <code>hosts</code> attribute to provide multiple hostnames, or subdomains. Each hostname generates a unique route for the app. <code>hosts</code> can be used in conjunction with <code>host</code>. If you define both attributes, Cloud Foundry creates routes for hostnames defined in both <code>host</code> and <code>hosts</code>.</p>
<pre>
---
...
hosts:
- app_host1
- app_host2
</pre>
<p>The command line option that overrides this attribute is <code>-n</code>.</p>

View File

@@ -0,0 +1,62 @@
<p>A single manifest can describe multiple applications. Another powerful technique is to create multiple manifests with inheritance. Here, manifests have parent-child relationships such that children inherit descriptions from a parent. Children can use inherited descriptions as-is, extend them, or override them.</p>
<p>Content in the child manifest overrides content in the parent manifest, if the two conflict.</p>
<p>This technique helps in these and other scenarios:</p>
<ul>
<li><p>An application has a set of different deployment modes, such as debug, local, and public. Each deployment mode is described in child manifests that extend the settings in a base parent manifest.</p></li>
<li><p>An application is packaged with a basic configuration described by a parent manifest. Users can extend the basic configuration by creating child manifests that add new properties or override those in the parent manifest.</p></li>
</ul>
<p>The benefits of multiple manifests with inheritance are similar to those of minimizing duplicated content within single manifests. With inheritance, though, we “promote” content by placing it in the parent manifest.</p>
<p>Every child manifest must contain an “inherit” line that points to the parent manifest. Place the inherit line immediately after the three dashes at the top of the child manifest. For example, every child of a parent manifest called <code>base-manifest.yml</code> begins like this:</p>
<pre>---
...
inherit: base-manifest.yml
</pre>
<p>You do not need to add anything to the parent manifest.</p>
<p>In the simple example below, a parent manifest gives each application minimal resources, while a production child manifest scales them up.</p>
<p><strong>simple-base-manifest.yml</strong></p>
<pre>---
path: .
domain: shared-domain.com
memory: 256M
instances: 1
services:
- singular-backend
# app-specific configuration
applications:
- name: springtock
host: 765shower
path: ./april/build/libs/april-weather.war
- name: wintertick
host: 321flurry
path: ./december/target/december-weather.war
</pre>
<p><strong>simple-prod-manifest.yml</strong></p>
<pre>---
inherit: simple-base-manifest.yml
applications:
- name:springstorm
memory: 512M
instances: 1
host: 765deluge
path: ./april/build/libs/april-weather.war
- name: winterblast
memory: 1G
instances: 2
host: 321blizzard
path: ./december/target/december-weather.war
</pre>
<p><class='note'><strong>Note</strong>: Inheritance can add an additional level of complexity to manifest creation and maintenance. Comments that precisely explain how the child manifest extends or overrides the descriptions in the parent manifest can alleviate this complexity.</class='note'>

View File

@@ -0,0 +1,11 @@
<p>Use the <code>instances</code> attribute to specify the number of app instances that you want to start upon push:</p>
<pre>
---
...
instances: 2
</pre>
<p>We recommend that you run at least two instances of any apps for which fault tolerance matters.</p>
<p>The command line option that overrides this attribute is <code>-i</code>.</p>

View File

@@ -0,0 +1,11 @@
<p>Use the <code>memory</code> attribute to specify the memory limit for all instances of an app. This attribute requires a unit of measurement: <code>M</code>, <code>MB</code>, <code>G</code>, or <code>GB</code>, in upper case or lower case. For example:</p>
<pre>
---
...
memory: 1024M
</pre>
<p>The default memory limit is 1G. You might want to specify a smaller limit to conserve quota space if you know that your app instances do not require 1G of memory.</p>
<p>The command line option that overrides this attribute is <code>-m</code>.</p>

View File

@@ -0,0 +1,10 @@
<p>The <code>name</code> attribute is the only required attribute
for an application in a manifest file. </p>
<p>This is an example of a minimal manifest:</p>
<pre>
---
applications:
- name: nifty-gui
</pre>

View File

@@ -0,0 +1,9 @@
<p>By default, if you do not provide a hostname, the URL for the app takes the form of <code>APP-NAME.DOMAIN</code>. If you want to override this and map the root domain to this app then you can set no-hostname as true.</p>
<pre>
---
...
no-hostname: true
</pre>
<p>The command line option that corresponds to this attribute is <code>--no-hostname</code>.</p>

View File

@@ -0,0 +1,18 @@
<p>By default, <code>cf push</code> assigns a route to every application. But some applications process data while running in the background, and should not be assigned routes.</p>
<p>You can use the <code>no-route</code> attribute with a value of <code>true</code> to prevent a route from being created for your application.</p>
<pre>
---
...
no-route: true
</pre>
<p>The command line option that corresponds to this attribute is <code>--no-route</code>.</p>
<p>If you find that an application which should not have a route does have one:</p>
<ol>
<li>Remove the route using the <code>cf unmap-route</code> command.</li>
<li>Push the app again with the <code>no-route: true</code> attribute in the manifest or the <code>--no-route</code> command line option.</li>
</ol>

View File

@@ -0,0 +1,9 @@
<p>You can use the <code>path</code> attribute to tell Cloud Foundry where to find your application. This is generally not necessary when you run <code>cf push</code> from the directory where an application is located.</p>
<pre>
---
...
path: path_to_application_bits
</pre>
<p>The command line option that overrides this attribute is <code>-p</code>.</p>

View File

@@ -0,0 +1,11 @@
<p>Use the <code>random-route</code> attribute to create a URL that includes the app name and
random words.
Use this attribute to avoid URL collision when pushing the same app to multiple spaces, or to avoid managing app URLs.</p>
<p>The command line option that corresponds to this attribute is <code>--random-route</code>.</p>
<pre>
---
...
random-route: true
</pre>

View File

@@ -0,0 +1,18 @@
<p>Applications can bind to services such as databases, messaging, and key-value stores.</p>
<p>Applications are deployed into App Spaces. An application can only bind to services instances that exist in the target App Space before the application is deployed.</p>
<p>The <code>services</code> block consists of a heading, then one or more service instance names.</p>
<p>Whoever creates the service chooses the service instance names. These names can convey logical information, as in <code>backend_queue</code>, describe the nature of the service, as in <code>mysql_5.x</code>, or do neither, as in the example below.</p>
<pre>
---
...
services:
- instance_ABC
- instance_XYZ
</pre>
<p>Binding to a service instance is a special case of setting an environment
variable, namely <code>VCAP_SERVICES</code>.

View File

@@ -0,0 +1,11 @@
<p>Use the <code>stack</code> attribute to specify which stack to deploy your application to.</p>
<p>To see a list of available stacks, run <code>cf stacks</code> from the cf cli.</p>
<pre>
---
...
stack: cflinuxfs2
</pre>
<p>The command line option that overrides this attribute is <code>-s</code>.</p>

View File

@@ -0,0 +1,14 @@
<p>The <code>timeout</code> attribute defines the number of seconds Cloud Foundry allocates for starting your application. </p>
<p>For example:</p>
<pre>
---
...
timeout: 80
</pre>
<p>You can increase the timeout length for very large apps that require more time to start. The default timeout is 60 seconds with an upper bound of 180 seconds.</p>
<p class="note"><strong>Note</strong>: Administrators can set the upper bound of the <code>maximum_health_check_timeout</code> property to any value. Any changes to Cloud Controller properties in the deployment manifest require running <code>bosh deploy</code>.</p>
<p>The command line option that overrides the timeout attribute for the shell is <code>-t</code>. Manifest values still apply to applications pushed to the deployment.</p>

View File

@@ -0,0 +1,144 @@
package org.springframework.ide.vscode.yaml;
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.ide.eclipse.cloudfoundry.manifest.editor.ManifestYmlSchema;
import org.springframework.ide.vscode.util.StringUtil;
import org.springframework.ide.vscode.yaml.schema.YTypeFactory.YBeanType;
import org.springframework.ide.vscode.yaml.schema.YTypeFactory.YSeqType;
import org.springframework.ide.vscode.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.yaml.util.DescriptionProviders;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
/**
* @author Kris De Volder
*/
public class ManifestYmlSchemaTest {
private static final String[] NESTED_PROP_NAMES = {
// "applications",
"buildpack",
"command",
"disk_quota",
"domain",
"domains",
"env",
"host",
"hosts",
// "inherit",
"instances",
"memory",
"name",
"no-hostname",
"no-route",
"path",
"random-route",
"services",
"stack",
"timeout"
};
private static final String[] TOPLEVEL_PROP_NAMES = {
"applications",
"buildpack",
"command",
"disk_quota",
"domain",
"domains",
"env",
// "host",
// "hosts",
"inherit",
"instances",
"memory",
// "name",
"no-hostname",
"no-route",
"path",
"random-route",
"services",
"stack",
"timeout"
};
ManifestYmlSchema schema = new ManifestYmlSchema(null);
@Test
public void toplevelProperties() throws Exception {
assertPropNames(schema.getTopLevelType().getProperties(), TOPLEVEL_PROP_NAMES);
assertPropNames(schema.getTopLevelType().getPropertiesMap(), TOPLEVEL_PROP_NAMES);
}
@Test
public void nestedProperties() throws Exception {
assertPropNames(getNestedProps(), NESTED_PROP_NAMES);
}
@Test
public void toplevelPropertiesHaveDescriptions() {
for (YTypedProperty p : schema.getTopLevelType().getProperties()) {
if (!p.getName().equals("applications")) {
assertHasRealDescription(p);
}
}
}
@Test
public void nestedPropertiesHaveDescriptions() {
for (YTypedProperty p : getNestedProps()) {
assertHasRealDescription(p);
}
}
//////////////////////////////////////////////////////////////////////////////
private void assertHasRealDescription(YTypedProperty p) {
String noDescriptionText = DescriptionProviders.NO_DESCRIPTION.get().toText();
String actual = p.getDescription().toText();
String msg = "Description missing for '"+p.getName()+"'";
assertTrue(msg, StringUtil.hasText(actual));
assertFalse(msg, noDescriptionText.equals(actual));
}
private List<YTypedProperty> getNestedProps() {
YSeqType applications = (YSeqType) schema.getTopLevelType().getPropertiesMap().get("applications").getType();
YBeanType application = (YBeanType) applications.getDomainType();
return application.getProperties();
}
private void assertPropNames(List<YTypedProperty> properties, String... expectedNames) {
assertEquals(ImmutableSet.copyOf(expectedNames), getNames(properties));
}
private void assertPropNames(Map<String, YTypedProperty> propertiesMap, String[] toplevelPropNames) {
assertEquals(ImmutableSet.copyOf(toplevelPropNames), ImmutableSet.copyOf(propertiesMap.keySet()));
}
private ImmutableSet<String> getNames(Iterable<YTypedProperty> properties) {
Builder<String> builder = ImmutableSet.builder();
for (YTypedProperty p : properties) {
builder.add(p.getName());
}
return builder.build();
}
}