Minimal support for V1 manifest schema
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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*/);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user