Dynamic CA and reconcile for stemcells definition attributes

This commit is contained in:
Kris De Volder
2017-07-24 16:58:10 -07:00
parent 30eab2b367
commit b13eefb594
5 changed files with 222 additions and 11 deletions

View File

@@ -19,14 +19,20 @@ import org.apache.commons.lang3.tuple.Pair;
import org.springframework.ide.vscode.bosh.models.CachingModelProvider;
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.bosh.models.StemcellModel;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.ValueParsers;
import org.springframework.ide.vscode.commons.yaml.ast.YamlAstCache;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
@@ -83,16 +89,18 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
private YType t_release_name_ref;
private YType t_instance_group_name_def;
private YType t_var_name_def;
private final YamlAstCache asts;
private final ASTTypeCache astTypes;
private DynamicModelProvider<CloudConfigModel> cloudConfigProvider;
private DynamicModelProvider<StemcellsModel> stemcellsProvider;
private List<Pair<YType, YType>> defAndRefTypes;
public BoshDeploymentManifestSchema(
ASTTypeCache astTypes,
YamlAstCache asts, ASTTypeCache astTypes,
DynamicModelProvider<CloudConfigModel> cloudConfigProvider,
DynamicModelProvider<StemcellsModel> stemcellsProvider
) {
this.asts = asts;
this.astTypes = astTypes;
this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider);
this.stemcellsProvider = new CachingModelProvider<>(stemcellsProvider);
@@ -144,9 +152,6 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
.parseWith(ValueParsers.NE_STRING);
YType t_stemcell_name_ref = f.yenumFromDynamicValues("StemcellName", (dc) -> stemcellsProvider.getModel(dc).getStemcellNames());
YType t_stemcell_os_ref = f.yenumFromDynamicValues("StemcellOs", (dc) -> stemcellsProvider.getModel(dc).getStemcellOss());
YAtomicType t_ip_address = f.yatomic("IPAddress"); //TODO: some kind of checking?
t_ip_address.parseWith(ValueParsers.NE_STRING);
@@ -187,8 +192,24 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
t_release.require(BoshConstraints.SHA1_REQUIRED_FOR_HTTP_URL);
YBeanType t_stemcell = f.ybean("Stemcell");
YType t_stemcell_name_ref = f.yenumFromDynamicValues("StemcellName", (dc) -> stemcellsProvider.getModel(dc).getStemcellNames());
YType t_stemcell_os_ref = f.yenumFromDynamicValues("StemcellOs", (dc) -> stemcellsProvider.getModel(dc).getStemcellOss());
YType t_stemcell_version_ref = f.yenumFromDynamicValues("StemcellVersion", (dc) -> {
StemcellModel currentStemcell = getCurrentStemcell(dc);
if (currentStemcell!=null) {
return stemcellsProvider.getModel(dc).getStemcells().stream()
.filter(sc -> StringUtil.hasText(sc.getVersion()))
.filter(currentStemcell.createVersionFilter())
.map(sc -> sc.getVersion())
.collect(CollectorUtil.toImmutableSet());
}
//Troubles determining the filter. So return all stemcells.
return stemcellsProvider.getModel(dc).getVersions();
});
addProp(t_stemcell, "alias", t_stemcell_alias_def).isRequired(true);
addProp(t_stemcell, "version", t_ne_string).isRequired(true);
addProp(t_stemcell, "version", t_stemcell_version_ref).isRequired(true);
addProp(t_stemcell, "name", t_stemcell_name_ref);
addProp(t_stemcell, "os", t_stemcell_os_ref);
t_stemcell.requireOneOf("name", "os");
@@ -256,6 +277,12 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
return v2Schema;
}
private StemcellModel getCurrentStemcell(DynamicSchemaContext dc) throws Exception {
YamlPath path = dc.getPath();
YamlFileAST ast = asts.getAst(dc.getDocument(), true);
return new StemcellModel(path.dropLast().traverseToNode(ast));
}
@Override
public YType getTopLevelType() {
return TOPLEVEL_TYPE;

View File

@@ -44,7 +44,7 @@ public class BoshLanguageServer extends SimpleLanguageServer {
SimpleTextDocumentService documents = getTextDocumentService();
ASTTypeCache astTypeCache = new ASTTypeCache();
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(astTypeCache, cloudConfigProvider, stemcellsProvider);
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(asts, astTypeCache, cloudConfigProvider, stemcellsProvider);
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
@@ -66,7 +66,6 @@ public class BoshLanguageServer extends SimpleLanguageServer {
documents.onDefinition(new BoshDefintionFinder(this, schema, asts, astTypeCache));
}
private void validateOnDocumentChange(IReconcileEngine engine, TextDocument doc) {
if (LanguageId.BOSH_DEPLOYMENT.equals(doc.getLanguageId())) {
validateWith(doc.getId(), engine);

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2017 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.bosh.models;
import java.util.function.Predicate;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.yaml.snakeyaml.nodes.Node;
/**
* A 'view' on a Yaml AST node interpreting it as a Stemcell declaration.
*/
public class StemcellModel {
private Node node;
public StemcellModel(Node node) {
this.node = node;
}
public String getName() {
return NodeUtil.getScalarProperty(node, "name");
}
public String getOs() {
return NodeUtil.getScalarProperty(node, "os");
}
/**
* Create an appropriate filter to limit the candidate stemcells for
* providing a version based on 'name' or 'os' values already defined in this stemcell.
*/
public Predicate<StemcellData> createVersionFilter() {
String name = getName();
String os = getOs();
if (StringUtil.hasText(name) && StringUtil.hasText(os)) {
//User shouldn't really specify both of these... but if they do... then
// let's do our best to generate proposals for the both of them.
return (sc) ->
name.equals(sc.getName()) || os.equals(sc.getOs());
} else if (StringUtil.hasText(name)) {
return (sc) ->
name.equals(sc.getName());
} else if (StringUtil.hasText(os)) {
return (sc) ->
os.equals(sc.getOs());
} else {
return (sc) ->
true;
}
}
}

View File

@@ -16,8 +16,11 @@ import static org.springframework.ide.vscode.languageserver.testharness.TestAsse
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.TimeoutException;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
@@ -27,12 +30,17 @@ import org.mockito.Mockito;
import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandStemcellsProvider;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.bosh.models.StemcellData;
import org.springframework.ide.vscode.bosh.models.StemcellsModel;
import org.springframework.ide.vscode.commons.util.ExternalCommand;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
@@ -45,7 +53,7 @@ public class BoshEditorTest {
@Before public void setup() throws Exception {
harness = new LanguageServerHarness(() -> {
return new BoshLanguageServer(cloudConfigProvider, stemcellsProvider)
return new BoshLanguageServer(cloudConfigProvider, (dc) -> stemcellsProvider.getModel(dc))
.setMaxCompletions(100);
},
LanguageId.BOSH_DEPLOYMENT
@@ -946,6 +954,124 @@ public class BoshEditorTest {
);
}
@Test public void contentAssistStemcellVersionFromDirector() throws Exception {
Editor editor;
stemcellsProvider = provideStemcellsFrom(
new StemcellData("ubuntu-agent", "123.4", "ubuntu"),
new StemcellData("ubuntu-agent", "222.2", "ubuntu"),
new StemcellData("centos-agent", "222.2", "centos"),
new StemcellData("centos-agent", "333.3", "centos")
);
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" name: centos-agent\n" +
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"222.2<*>", "333.3<*>"
);
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" os: ubuntu\n" +
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"123.4<*>", "222.2<*>"
);
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"123.4<*>", "222.2<*>", "333.3<*>"
);
//when os or name are 'bogus' at least suggest proposals based on other prop
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" name: centos-agent\n" +
" os: bogus\n" +
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"222.2<*>", "333.3<*>"
);
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" os: centos\n" +
" name: bogus\n" +
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"222.2<*>", "333.3<*>"
);
//when the os and name disagree, merge the proposals for both:
editor = harness.newEditor(
"stemcells:\n" +
"- alias: good\n" +
" os: centos\n" + //Contradicts the name
" name: ubuntu-agent\n" + //Contradicts the os
" version: <*>\n"
);
editor.assertContextualCompletions("<*>",
"123.4<*>", "222.2<*>", "333.3<*>"
);
}
@Test public void reconcileStemcellVersionFromDirector() throws Exception {
Editor editor;
stemcellsProvider = provideStemcellsFrom(
new StemcellData("ubuntu-agent", "123.4", "ubuntu"),
new StemcellData("ubuntu-agent", "222.2", "ubuntu"),
new StemcellData("centos-agent", "222.2", "centos"),
new StemcellData("centos-agent", "333.3", "centos")
);
editor = harness.newEditor(
"stemcells:\n" +
"- alias: aaa\n" +
" name: centos-agent\n" +
" version: 222.2\n" +
"- alias: bbb\n" +
" name: centos-agent\n" +
" version: 123.4\n" +
"- alias: ddd\n" +
" os: ubuntu\n" +
" version: 333.3\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems(
"123.4|unknown 'StemcellVersion'. Valid values are: [222.2, 333.3]",
"333.3|unknown 'StemcellVersion'. Valid values are: [123.4, 222.2]"
);
}
private DynamicModelProvider<StemcellsModel> provideStemcellsFrom(StemcellData... stemcellData) {
return new BoshCommandStemcellsProvider() {
@Override
protected String executeCommand(ExternalCommand command) throws Exception {
String rows = mapper.writeValueAsString(stemcellData);
return "{\n" +
" \"Tables\": [\n" +
" {\n" +
" \"Rows\": " + rows +
" }\n" +
" ]\n" +
"}";
}
};
}
@Test public void contentAssistVMtype() throws Exception {
Editor editor = harness.newEditor(
"name: foo\n" +

View File

@@ -10,7 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.reconcile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -30,9 +29,7 @@ import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Multimap;