Minimal support for V1 manifest schema

This commit is contained in:
Kris De Volder
2017-07-13 10:34:10 -07:00
parent 8c85693f62
commit c2a4ee258d
5 changed files with 171 additions and 36 deletions

View File

@@ -10,31 +10,43 @@
*******************************************************************************/
package org.springframework.ide.vscode.bosh;
import java.util.Map;
import java.util.UUID;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.util.ValueParsers;
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;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YContextSensitive;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import com.google.common.collect.ImmutableSet;
/**
* @author Kris De Volder
*/
public class BoshDeploymentManifestSchema implements YamlSchema {
private final AbstractType TOPLEVEL_TYPE;
private final YBeanType V2_TOPLEVEL_TYPE;
private final YBeanType V1_TOPLEVEL_TYPE;
private final YContextSensitive TOPLEVEL_TYPE;
private final YTypeUtil TYPE_UTIL;
private static final ImmutableSet<String> DEPRECATED_V1_PROPS = ImmutableSet.of("resource_pools", "networks", "compilation", "jobs", "disk_pools", "cloud_provider");
private static final ImmutableSet<String> SHARED_V1_V2_PROPS = ImmutableSet.of("name", "director_uuid", "releases", "update", "properties");
//Note: 'director_uuid' is also deprecated. But its treated separately since it is deprecated and ignored by V2 client no matter what (i.e. deprecated in both schemas)
public final YTypeFactory f = new YTypeFactory()
.enableTieredProposals(false);
.enableTieredProposals(false)
.suggestDeprecatedProperties(false);
public final YType t_string = f.yatomic("String");
public final YType t_ne_string = f.yatomic("String")
.parseWith(ValueParsers.NE_STRING);
@@ -56,9 +68,34 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
public BoshDeploymentManifestSchema() {
TYPE_UTIL = f.TYPE_UTIL;
TOPLEVEL_TYPE = f.ybean("BoshDeploymentManifest");
addProp(TOPLEVEL_TYPE, "name", t_ne_string).isPrimary(true);
addProp(TOPLEVEL_TYPE, "director_uuid", t_uuid).isDeprecated(
V2_TOPLEVEL_TYPE = createV2Schema();
V1_TOPLEVEL_TYPE = createV1Schema(V2_TOPLEVEL_TYPE);
TOPLEVEL_TYPE = f.contextAware("DeploymenManifestV1orV2", (dc) -> {
boolean looksLikeV1 = dc.getDefinedProperties().stream().anyMatch(DEPRECATED_V1_PROPS::contains);
return looksLikeV1 ? V1_TOPLEVEL_TYPE : V2_TOPLEVEL_TYPE;
});
}
private YBeanType createV1Schema(AbstractType v2Schema) {
YBeanType v1Schema = f.ybean("DeploymentManifestV1");
Map<String, YTypedProperty> v2properties = v2Schema.getPropertiesMap();
ImmutableSet<String> v1Props = ImmutableSet.<String>builder()
.addAll(DEPRECATED_V1_PROPS)
.addAll(SHARED_V1_V2_PROPS)
.build();
for (String name : v1Props) {
YTypedProperty prop = v2properties.get(name);
Assert.isNotNull(prop);
v1Schema.addProperty(prop);
}
return v1Schema;
}
private YBeanType createV2Schema() {
YBeanType v2Schema = f.ybean("BoshDeploymentManifest");
addProp(v2Schema, "name", t_ne_string).isPrimary(true);
addProp(v2Schema, "director_uuid", t_uuid).isDeprecated(
"bosh v2 CLI no longer checks or requires director_uuid in the deployment manifest. " +
"To achieve similar safety make sure to give unique deployment names across environments."
);
@@ -100,7 +137,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
YBeanType t_release = f.ybean("Release");
addProp(t_release, "name", t_ne_string).isPrimary(true);
addProp(t_release, "version", t_version).isRequired(true);
addProp(TOPLEVEL_TYPE, "releases", f.yseq(t_release)).isRequired(true);
addProp(v2Schema, "releases", f.yseq(t_release)).isRequired(true);
YBeanType t_stemcell = f.ybean("Stemcell");
addProp(t_stemcell, "alias", t_ne_string).isRequired(true);
@@ -108,7 +145,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
addProp(t_stemcell, "name", t_ne_string);
addProp(t_stemcell, "os", t_ne_string);
t_stemcell.requireOneOf("name", "os");
addProp(TOPLEVEL_TYPE, "stemcells", f.yseq(t_stemcell)).isRequired(true);
addProp(v2Schema, "stemcells", f.yseq(t_stemcell)).isRequired(true);
YBeanType t_update = f.ybean("Update");
addProp(t_update, "canaries", t_strictly_pos_integer).isRequired(true);
@@ -116,7 +153,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
addProp(t_update, "canary_watch_time", t_integer_or_range).isRequired(true);
addProp(t_update, "update_watch_time", t_integer_or_range).isRequired(true);
addProp(t_update, "serial", t_boolean);
addProp(TOPLEVEL_TYPE, "update", t_update).isRequired(true);
addProp(v2Schema, "update", t_update).isRequired(true);
YBeanType t_job = f.ybean("Job");
addProp(t_job, "name", t_ne_string).isRequired(true);
@@ -142,17 +179,21 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
addProp(t_instance_group, "properties", t_params).isDeprecated("Deprecated in favor of job level properties and links");
addProp(t_instance_group, "env", t_instance_group_env);
addProp(TOPLEVEL_TYPE, "instance_groups", f.yseq(t_instance_group)).isRequired(true);
addProp(TOPLEVEL_TYPE, "properties", t_params).isDeprecated("Deprecated in favor of job level properties and links");
addProp(v2Schema, "instance_groups", f.yseq(t_instance_group)).isRequired(true);
addProp(v2Schema, "properties", t_params).isDeprecated("Deprecated in favor of job level properties and links");
YBeanType t_variable = f.ybean("Variable");
addProp(t_variable, "name", t_ne_string).isPrimary(true);
addProp(t_variable, "type", f.yenum("VariableType", "certificate", "password", "rsa", "ssh")).isRequired(true);
addProp(t_variable, "options", t_params);
addProp(TOPLEVEL_TYPE, "variables", f.yseq(t_variable));
addProp(v2Schema, "variables", f.yseq(t_variable));
addProp(TOPLEVEL_TYPE, "tags", t_params);
addProp(v2Schema, "tags", t_params);
for (String v1Prop : DEPRECATED_V1_PROPS) {
addProp(v2Schema, v1Prop, t_any).isDeprecated("Deprecated: '"+v1Prop+"' is a V1 schema property. Consider migrating your deployment manifest to V2");
}
return v2Schema;
}
@Override

View File

@@ -10,8 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import static org.springframework.ide.vscode.languageserver.testharness.Editor.PLAIN_COMPLETION;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.ide.vscode.bosh.BoshLanguageServer;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
@@ -19,8 +20,6 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import static org.springframework.ide.vscode.languageserver.testharness.Editor.*;
public class BoshEditorTest {
LanguageServerHarness harness;
@@ -115,7 +114,7 @@ public class BoshEditorTest {
editor.assertHoverContains("tags", "Specifies key value pairs to be sent to the CPI for VM tagging");
}
@Test public void toplevelV2PropertyCompletions() throws Exception {
@Test public void toplevelPropertyCompletions() throws Exception {
Editor editor = harness.newEditor(
"<*>"
);
@@ -149,12 +148,13 @@ public class BoshEditorTest {
"name: blah\n" +
"variables:\n" +
"- name: <*>"
, // ============
"name: blah\n" +
"director_uuid: <*>"
, // ============
"name: blah\n" +
"properties:\n <*>"
// Below completions are suppressed because they are deprecated
// , // ============
// "name: blah\n" +
// "director_uuid: <*>"
// , // ============
// "name: blah\n" +
// "properties:\n <*>"
);
}
@@ -312,11 +312,12 @@ public class BoshEditorTest {
"- name: foo-group\n" +
" vm_type: <*>"
, // =============
"instance_groups:\n" +
"- name: foo-group\n" +
" properties:\n" +
" <*>"
// , // =============
// Not suggested because its deprecated:
// "instance_groups:\n" +
// "- name: foo-group\n" +
// " properties:\n" +
// " <*>"
);
}
@@ -517,4 +518,82 @@ public class BoshEditorTest {
editor.assertHoverContains("type", "Type of a variable");
editor.assertHoverContains("options", "Specifies generation options");
}
}
@Test public void tolerateV1Manifests() throws Exception {
Editor editor = harness.newEditor(
"---\n" +
"name: my-redis-deployment\n" +
"director_uuid: 1234abcd-5678-efab-9012-3456cdef7890\n" +
"\n" +
"releases:\n" +
"- {name: redis, version: 12}\n" +
"\n" +
"resource_pools:\n" +
"- name: redis-servers\n" +
" network: default\n" +
" stemcell:\n" +
" name: bosh-aws-xen-ubuntu-trusty-go_agent\n" +
" version: 2708\n" +
" cloud_properties:\n" +
" instance_type: m1.small\n" +
" availability_zone: us-east-1c\n" +
"\n" +
"disk_pools: []\n" +
"\n" +
"networks:\n" +
"- name: default\n" +
" type: manual\n" +
" subnets:\n" +
" - range: 10.10.0.0/24\n" +
" gateway: 10.10.0.1\n" +
" static:\n" +
" - 10.10.0.16 - 10.10.0.18\n" +
" reserved:\n" +
" - 10.10.0.2 - 10.10.0.15\n" +
" dns: [10.10.0.6]\n" +
" cloud_properties:\n" +
" subnet: subnet-d597b993\n" +
"\n" +
"compilation:\n" +
" workers: 2\n" +
" network: default\n" +
" reuse_compilation_vms: true\n" +
" cloud_properties:\n" +
" instance_type: c1.medium\n" +
" availability_zone: us-east-1c\n" +
"\n" +
"update:\n" +
" canaries: 1\n" +
" max_in_flight: 3\n" +
" canary_watch_time: 15000-30000\n" +
" update_watch_time: 15000-300000\n" +
"\n" +
"jobs:\n" +
"- name: redis-master\n" +
" instances: 1\n" +
" templates:\n" +
" - {name: redis-server, release: redis}\n" +
" persistent_disk: 10_240\n" +
" resource_pool: redis-servers\n" +
" networks:\n" +
" - name: default\n" +
"\n" +
"- name: redis-slave\n" +
" instances: 2\n" +
" templates:\n" +
" - {name: redis-server, release: redis}\n" +
" persistent_disk: 10_240\n" +
" resource_pool: redis-servers\n" +
" networks:\n" +
" - name: default\n" +
"\n" +
"properties:\n" +
" redis:\n" +
" max_connections: 10\n" +
"\n" +
"cloud_provider: {}"
);
editor.ignoreProblem(YamlSchemaProblems.DEPRECATED_PROPERTY);
editor.assertProblems(/*NONE*/);
}
}

View File

@@ -70,18 +70,18 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
this.type = relaxedType;
}
public YTypeAssistContext(YTypeAssistContext parent, YamlPath contextPath, YType YType, YTypeUtil typeUtil) {
public YTypeAssistContext(YTypeAssistContext parent, YamlPath contextPath, YType type, YTypeUtil typeUtil) {
super(parent.getDocument(), parent.documentSelector, contextPath);
this.parent = parent;
this.typeUtil = typeUtil;
this.type = typeUtil.inferMoreSpecificType(YType, getSchemaContext());
this.type = typeUtil.inferMoreSpecificType(type, getSchemaContext());
}
public YTypeAssistContext(TopLevelAssistContext parent, int documentSelector, YType type, YTypeUtil typeUtil) {
super(parent.getDocument(), documentSelector, YamlPath.EMPTY);
this.parent = parent;
this.typeUtil = typeUtil;
this.type = type;
this.type = typeUtil.inferMoreSpecificType(type, getSchemaContext());
}
@Override
@@ -114,9 +114,10 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
List<List<YTypedProperty>> tieredProperties = sortIntoTiers(allProperties);
Set<String> definedProps = dynamicCtxt.getDefinedProperties();
List<ICompletionProposal> proposals = new ArrayList<>();
boolean suggestDeprecated = typeUtil.suggestDeprecatedProperties();
for (List<YTypedProperty> thisTier : tieredProperties) {
List<YTypedProperty> undefinedProps = thisTier.stream()
.filter(p -> !definedProps.contains(p.getName()))
.filter(p -> !definedProps.contains(p.getName()) && (suggestDeprecated || !p.isDeprecated()))
.collect(Collectors.toList());
if (!undefinedProps.isEmpty()) {
for (YTypedProperty p : undefinedProps) {

View File

@@ -52,10 +52,8 @@ import reactor.core.publisher.Flux;
*/
public class YTypeFactory {
/**
* Configuration option for the type-based completion engine.
*/
private boolean enableTieredOptionalPropertyProposals = true;
private boolean suggestDeprecatedProperties = true;
private static class Deprecation {
final String errorMsg;
@@ -239,6 +237,11 @@ public class YTypeFactory {
public boolean tieredOptionalPropertyProposals() {
return enableTieredOptionalPropertyProposals;
}
@Override
public boolean suggestDeprecatedProperties() {
return suggestDeprecatedProperties;
}
};
/////////////////////////////////////////////////////////////////////////////////////
@@ -916,4 +919,9 @@ public class YTypeFactory {
return this;
}
public YTypeFactory suggestDeprecatedProperties(boolean enable) {
this.suggestDeprecatedProperties = enable;
return this;
}
}

View File

@@ -56,4 +56,10 @@ public interface YTypeUtil {
* suggested until required ones are all defined)
*/
boolean tieredOptionalPropertyProposals();
/**
* Config option for type-based completion engine. This enables/disables
* whether engine should generate proposals for deprecated properties (true),
* or suppress them (false).
*/
boolean suggestDeprecatedProperties();
}