From faa45b76c667622fe52a9cd61d8090eaaf99b02c Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Wed, 9 Aug 2017 12:25:57 -0700 Subject: [PATCH] CloudConfig 'manual network' schema --- .../vscode/bosh/BoshCloudConfigSchema.java | 116 +++++++++-- .../ide/vscode/bosh/models/BoshModels.java | 11 ++ .../cloud-config/ManualNetwork/subnets.md | 1 + .../resources/cloud-config/Network/name.md | 1 + .../resources/cloud-config/Network/type.md | 1 + .../cloud-config/Subnet[Manual]/az.md | 1 + .../cloud-config/Subnet[Manual]/azs.md | 1 + .../Subnet[Manual]/cloud_properties.md | 11 ++ .../cloud-config/Subnet[Manual]/dns.md | 1 + .../cloud-config/Subnet[Manual]/gateway.md | 1 + .../cloud-config/Subnet[Manual]/range.md | 1 + .../cloud-config/Subnet[Manual]/reserved.md | 1 + .../cloud-config/Subnet[Manual]/static.md | 1 + .../ide/vscode/bosh/BoshEditorTest.java | 183 +++++++++++++++++- .../languageserver/testharness/Editor.java | 4 +- 15 files changed, 307 insertions(+), 28 deletions(-) create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/ManualNetwork/subnets.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Network/name.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Network/type.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/az.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/azs.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/cloud_properties.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/dns.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/gateway.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/range.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/reserved.md create mode 100644 headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/static.md diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java index 518ccfc52..e19a47c40 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/BoshCloudConfigSchema.java @@ -15,7 +15,9 @@ import java.util.Collection; import org.apache.commons.lang3.tuple.Pair; import org.springframework.ide.vscode.bosh.models.BoshModels; import org.springframework.ide.vscode.commons.util.CollectorUtil; +import org.springframework.ide.vscode.commons.util.Lazy; import org.springframework.ide.vscode.commons.util.PartialCollection; +import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.ValueParsers; import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache; import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems; @@ -24,40 +26,63 @@ 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.YTypeUtil; +import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty; +import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints; import com.google.common.collect.ImmutableList; -import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; -import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema; - public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema { private final YBeanType toplevelType; private final YType t_az_ref; + private final YType t_az_def; private final YType t_vm_type_ref; private final YType t_vm_type_def; private final YType t_network_ref; + private AbstractType t_network_def; private Collection definitionTypes; - private Collection> defAndRefTypes; + private final Lazy>> defAndRefTypes = new Lazy<>(); + private AbstractType t_any; + private AbstractType t_ne_string; + private YAtomicType t_boolean; + private AbstractType t_pos_integer; + private YType t_params; + private YType t_ip_address; + private YType t_ip_range; + private YType t_ip_address_or_range; public BoshCloudConfigSchema(YTypeFactory f, BoshModels models) { super(f); - AbstractType t_any = f.yany("Object"); - AbstractType t_ne_string = f.yatomic("String") + t_any = f.yany("Object"); + t_ne_string = f.yatomic("String") .parseWith(ValueParsers.NE_STRING); - YAtomicType t_boolean = f.yenum("boolean", "true", "false"); - AbstractType t_pos_integer = f.yatomic("Positive Integer") + t_boolean = f.yenum("boolean", "true", "false"); + t_pos_integer = f.yatomic("Positive Integer") .parseWith(ValueParsers.POS_INTEGER); - YType t_params = f.ymap(t_ne_string, t_any); + t_params = f.ymap(t_ne_string, t_any); - t_az_ref = t_ne_string; + t_az_def = f.yatomic("AZName").parseWith(ValueParsers.NE_STRING); + t_az_ref = f.yenumFromDynamicValues("AZName", (dc) -> PartialCollection.compute(() -> + models.astTypes.getDefinedNames(dc, t_az_def) + )); t_vm_type_def = f.yatomic("VMTypeName").parseWith(ValueParsers.NE_STRING); t_vm_type_ref = f.yenumFromDynamicValues("VMTypeName", (dc) -> PartialCollection.compute(() -> models.astTypes.getDefinedNames(dc, t_vm_type_def) )); + t_network_def = f.yatomic("NetworkName").parseWith(ValueParsers.NE_STRING); t_network_ref = t_ne_string; + t_ip_address = t_ne_string; //TODO? Syntax for ip addresses like 192.168.1.22 ? + t_ip_range = t_ne_string; //TODO? Syntax for ip ranges like 10.10.0.0/22 ? + t_ip_address_or_range = t_ne_string; //TODO? + + YType t_network = createNetworkBlockSchema(models); + + YBeanType t_az = f.ybean("AZ"); + addProp(t_az, "name", t_az_def).isRequired(true); + addProp(t_az, "cloud_properties", t_params); YBeanType t_compilation = f.ybean("Compilation"); addProp(t_compilation, "workers", t_pos_integer).isRequired(true); @@ -71,8 +96,8 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema { addProp(t_vm_type, "cloud_properties", t_params); this.toplevelType = f.ybean("CloudConfig"); - addProp(toplevelType, "azs", t_any).isRequired(true); - addProp(toplevelType, "networks", t_any).isRequired(true); + addProp(toplevelType, "azs", f.yseq(t_az)).isRequired(true); + addProp(toplevelType, "networks", f.yseq(t_network)).isRequired(true); addProp(toplevelType, "vm_types", f.yseq(t_vm_type)).isRequired(true); addProp(toplevelType, "vm_extensions", t_any); addProp(toplevelType, "disk_types", t_any).isRequired(true); @@ -85,18 +110,73 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema { } + private YType createNetworkBlockSchema(BoshModels models) { + AbstractType t_manual_nw = f.ybean("ManualNetwork"); + { + YBeanType t_subnet = f.ybean("Subnet[Manual]"); + addProp(t_subnet, "range", t_ip_range).isRequired(true); + addProp(t_subnet, "gateway", t_ip_address).isRequired(true); + addProp(t_subnet, "dns", f.yseq(t_ip_address)); + addProp(t_subnet, "reserved", f.yseq(t_ip_address_or_range)); + addProp(t_subnet, "static", f.yseq(t_ip_address_or_range)); + addProp(t_subnet, "az", t_az_ref); + addProp(t_subnet, "azs", f.yseq(t_az_ref)); + addProp(t_subnet, "cloud_properties", t_params); + + addProp(t_manual_nw, "subnets", f.yseq(t_subnet)).isRequired(true); + } + + AbstractType t_vip_nw = f.ybean("VipNetwork"); + addProp(t_vip_nw, "cloud_properties", t_params); + + AbstractType t_dynamic_nw = f.ybean("DynamicNetwork"); + addProp(t_dynamic_nw, "dns", f.yseq(t_ip_address)); + addProp(t_dynamic_nw, "cloud_properties", t_params); + + t_dynamic_nw.require(Constraints.mutuallyExclusive("dns", "subnets")); + t_dynamic_nw.require(Constraints.mutuallyExclusive("cloud_properties", "subnets")); + + AbstractType t_network = f.contextAware("Network", dc -> { + String type = models.getTypeTag(dc); + if (StringUtil.hasText(type)) { + switch (type) { + case "manual": + return t_manual_nw; + case "dynamic": + return t_dynamic_nw; + case "vip": + return t_vip_nw; + default: + } + } + return null; + }) + .treatAsBean(); + + //Add shared properties to the 'super' type. + addProp(t_network, "name", t_network_def).isPrimary(true); + addProp(t_network, "type", f.yenum("NetworkType", "manual", "dynamic", "vip")).isRequired(true); + + //Add shared properties to all 'sub' types. + for (AbstractType subtype : ImmutableList.of(t_dynamic_nw, t_manual_nw, t_vip_nw)) { + for (YTypedProperty sharedProp : t_network.getProperties()) { + subtype.addProperty(sharedProp); + } + } + return t_network; + } + /** * @return Pairs of types. Each pair contains a 'def' type and a 'ref' type. Nodes with the ref-type * shall be interpreted as reference to a corresponding node with the 'def' type if the def and ref node * contain the same scalar value. */ public Collection> getDefAndRefTypes() { - if (defAndRefTypes==null) { - defAndRefTypes = ImmutableList.of( - Pair.of(t_vm_type_def, t_vm_type_ref) - ); - } - return defAndRefTypes; + return defAndRefTypes.load(() -> ImmutableList.of( + Pair.of(t_vm_type_def, t_vm_type_ref), + Pair.of(t_network_def, t_network_ref), + Pair.of(t_az_def, t_az_ref) + )); } public Collection getDefinitionTypes() { diff --git a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshModels.java b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshModels.java index ad8e5d236..258b8c800 100644 --- a/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshModels.java +++ b/headless-services/bosh-language-server/src/main/java/org/springframework/ide/vscode/bosh/models/BoshModels.java @@ -10,8 +10,11 @@ *******************************************************************************/ package org.springframework.ide.vscode.bosh.models; +import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil; 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.reconcile.ASTTypeCache; +import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext; public class BoshModels { public final YamlAstCache asts = new YamlAstCache(); @@ -28,4 +31,12 @@ public class BoshModels { this.releasesProvider = releasesProvider; } + public String getTypeTag(DynamicSchemaContext dc) { + YamlFileAST ast = asts.getSafeAst(dc.getDocument(), true); + if (ast!=null) { + return NodeUtil.asScalar(dc.getPath().thenValAt("type").traverseToNode(ast)); + } + return null; + } + } diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/ManualNetwork/subnets.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/ManualNetwork/subnets.md new file mode 100644 index 000000000..edbab4520 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/ManualNetwork/subnets.md @@ -0,0 +1 @@ +*Required*. Lists subnets in this network. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Network/name.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Network/name.md new file mode 100644 index 000000000..8cfc13169 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Network/name.md @@ -0,0 +1 @@ +*Required*. Name used to reference this network configuration. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Network/type.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Network/type.md new file mode 100644 index 000000000..aa16fec66 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Network/type.md @@ -0,0 +1 @@ +*Required*. The type of configuration. Should be one of `manual`, `dynamic` or `vip`. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/az.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/az.md new file mode 100644 index 000000000..7cdefd3f4 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/az.md @@ -0,0 +1 @@ +AZ associated with this subnet (should only be used when using [first class AZs](https://bosh.io/docs/azs.html)). Example: `z1`. Available in v241+. diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/azs.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/azs.md new file mode 100644 index 000000000..dd793f6fe --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/azs.md @@ -0,0 +1 @@ +List of AZs associated with this subnet (should only be used when using [first class AZs](https://bosh.io/docs/azs.html)). Example: `[z1, z2]`. Available in v241+. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/cloud_properties.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/cloud_properties.md new file mode 100644 index 000000000..7d246f597 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/cloud_properties.md @@ -0,0 +1,11 @@ +Describes any IaaS-specific properties for the subnet. Default is {} (empty Hash). + +See: + +- [AWS CPI network cloud properties](https://bosh.io/docs/aws-cpi.html#networks) +- [Azure CPI network cloud properties](https://bosh.io/docs/azure-cpi.html#networks) +- [OpenStack CPI network cloud properties](https://bosh.io/docs/openstack-cpi.html#networks) +- [Softlayer CPI network cloud properties](https://bosh.io/docs/softlayer-cpi.html#networks) +- [Google Cloud Platform CPI network cloud properties](https://bosh.io/docs/google-cpi.html#networks) +- [vSphere CPI network cloud properties](https://bosh.io/docs/vsphere-cpi.html#networks) +- [vCloud CPI network cloud properties](https://bosh.io/docs/vcloud-cpi.html#networks) \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/dns.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/dns.md new file mode 100644 index 000000000..1882b628d --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/dns.md @@ -0,0 +1 @@ +DNS IP addresses for this subnet. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/gateway.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/gateway.md new file mode 100644 index 000000000..ab58138bb --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/gateway.md @@ -0,0 +1 @@ +*Required*. Subnet gateway IP. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/range.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/range.md new file mode 100644 index 000000000..b6081be07 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/range.md @@ -0,0 +1 @@ +*Required*. Subnet IP range that includes all IPs from this subnet. diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/reserved.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/reserved.md new file mode 100644 index 000000000..263b57e78 --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/reserved.md @@ -0,0 +1 @@ +Array of reserved IPs and/or IP ranges. BOSH does not assign IPs from this range to any VM. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/static.md b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/static.md new file mode 100644 index 000000000..13bc6d66f --- /dev/null +++ b/headless-services/bosh-language-server/src/main/resources/cloud-config/Subnet[Manual]/static.md @@ -0,0 +1 @@ +Array of static IPs and/or IP ranges. BOSH assigns IPs from this range to jobs requesting static IPs. Only IPs specified here can be used for static IP reservations. \ No newline at end of file diff --git a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java index 6ffc81813..69bee0726 100644 --- a/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java +++ b/headless-services/bosh-language-server/src/test/java/org/springframework/ide/vscode/bosh/BoshEditorTest.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.bosh; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -1915,13 +1916,8 @@ public class BoshEditorTest { "compilation:\n" + " <*>" ); - editor.assertContextualCompletions(PLAIN_COMPLETION.or(SNIPPET_COMPLETION), + editor.assertContextualCompletions(PLAIN_COMPLETION.and(SNIPPET_COMPLETION.negate()), "<*>" - , // ==> - "workers: $1\n" + - " az: $2\n" + - " vm_type: $3\n" + - " network: $4<*>" , "az: <*>" , @@ -1933,6 +1929,14 @@ public class BoshEditorTest { , "workers: <*>" ); + editor.assertContextualCompletions(PLAIN_COMPLETION.and(SNIPPET_COMPLETION), + "<*>" + , // ==> + "workers: $1\n" + + " az: $2\n" + + " vm_type: $3\n" + + " network: $4<*>" + ); editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, "compilation:\n" + @@ -1946,6 +1950,7 @@ public class BoshEditorTest { editor.assertProblems( "not-int|NumberFormatException", "not-bool|unknown 'boolean'", + "z1|unknown 'AZName'", "default|unknown 'VMTypeName'" ); editor.assertHoverContains("workers", "The maximum number of compilation VMs"); @@ -2037,7 +2042,9 @@ public class BoshEditorTest { " network: private" ); editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); - editor.assertProblems(/*NONE*/); + editor.assertProblems( + "z1|unknown 'AZName'" + ); editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, "vm_types:\n" + @@ -2057,7 +2064,167 @@ public class BoshEditorTest { " network: private" ); editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY); - editor.assertProblems("woot-vm|unknown 'VMTypeName'"); + editor.assertProblems( + "z1|unknown 'AZName'", + "woot-vm|unknown 'VMTypeName'" + ); + } + + @Test public void cloudconfig_network_ca() throws Exception { + Editor editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "networks:\n" + + "- <*>" + ); + editor.assertCompletions(SNIPPET_COMPLETION.negate(), + "networks:\n" + + "- name: <*>" + ); + editor.assertCompletions(SNIPPET_COMPLETION, + "networks:\n" + + "- name: $1\n" + + " type: $2<*>" + ); + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "networks:\n" + + "- name: foo\n" + + " <*>" + ); + editor.assertCompletions(PLAIN_COMPLETION, + "networks:\n" + + "- name: foo\n" + + " type: <*>" + ); + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "networks:\n" + + "- name: foo\n" + + " type: <*>" + ); + editor.assertContextualCompletions(PLAIN_COMPLETION, + "<*>" + , // => + "dynamic<*>", + "manual<*>", + "vip<*>" + ); + } + + @Test public void cloudconfig_manual_network_ca() throws Exception { + Editor editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "networks:\n" + + "- name: foo\n" + + " type: manual\n" + + " <*>" + ); + editor.assertContextualCompletions(PLAIN_COMPLETION, + "<*>" + , // => + "subnets:\n" + //non-snippet + " - <*>" + , + "subnets:\n" + //snippet + " - range: $1\n" + + " gateway: $2<*>" + ); + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "networks:\n" + + "- name: foo\n" + + " type: manual\n" + + " subnets:\n" + + " - <*>" + ); + editor.assertContextualCompletions(PLAIN_COMPLETION.and(SNIPPET_COMPLETION.negate()), + "<*>" + , // ==> + "az: <*>" + , + "azs:\n" + + " - <*>" + , + "cloud_properties:\n" + + " <*>" + , + "dns:\n" + + " - <*>" + , + "gateway: <*>" + , + "range: <*>" + , + "reserved:\n" + + " - <*>" + , + "static:\n" + + " - <*>" + ); + + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "azs:\n" + + "- name: zone-1\n" + + "- name: zone-2\n" + + "- name: zone-3\n" + + "networks:\n" + + "- name: foo\n" + + " type: manual\n" + + " subnets:\n" + + " - az: <*>" + ); + editor.assertContextualCompletions("<*>", "zone-1<*>", "zone-2<*>", "zone-3<*>"); + editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "azs:\n" + + "- name: zone-1\n" + + "- name: zone-2\n" + + "- name: zone-3\n" + + "networks:\n" + + "- name: foo\n" + + " type: manual\n" + + " subnets:\n" + + " - azs:\n" + + " - <*>" + ); + editor.assertContextualCompletions("<*>", "zone-1<*>", "zone-2<*>", "zone-3<*>"); + + } + + @Test public void cloudconfig_manual_network_hovers() throws Exception { + Editor editor = harness.newEditor(LanguageId.BOSH_CLOUD_CONFIG, + "networks:\n" + + "- name: my-network\n" + + " type: manual\n" + + "\n" + + " subnets:\n" + + " - range: 10.10.0.0/24\n" + + " gateway: 10.10.0.1\n" + + " dns: [10.10.0.2]\n" + + "\n" + + " # IPs that will not be used for anything\n" + + " reserved: [10.10.0.2-10.10.0.10]\n" + + " az: z1\n" + + "\n" + + " cloud_properties: {subnet: subnet-9be6c3f7}\n" + + "\n" + + " - range: 10.10.1.0/24\n" + + " gateway: 10.10.1.1\n" + + " dns: [10.10.1.2]\n" + + "\n" + + " static: [10.10.1.11-10.10.1.20]\n" + + "\n" + + " azs: [z2, z3]\n" + + " cloud_properties: {subnet: subnet-9be6c6gh}" + ); + editor.assertHoverContains("name", "Name used to reference this network configuration"); + editor.assertHoverContains("type", "The type of configuration. Should be one of `manual`, `dynamic` or `vip`"); + editor.assertHoverContains("subnets", "Lists subnets in this network"); + editor.assertHoverContains("range", "Subnet IP range that includes all IPs from this subnet"); + editor.assertHoverContains("gateway", "Subnet gateway IP"); + editor.assertHoverContains("dns", "DNS IP addresses for this subnet"); + editor.assertHoverContains("reserved", "Array of reserved IPs and/or IP ranges. BOSH does not assign IPs from this range to any VM"); + editor.assertHoverContains("static", "Array of static IPs and/or IP ranges."); + editor.assertHoverContains("az", "AZ associated with this subnet"); + editor.assertHoverContains("azs", "List of AZs associated with this subnet"); + editor.assertHoverContains("cloud_properties", "Describes any IaaS-specific properties for the subnet"); } } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index ba2449220..aa8e60ac5 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -61,10 +61,10 @@ public class Editor { || c.getLabel().startsWith(Unicodes.LEFT_ARROW+" ") || c.getLabel().startsWith(Unicodes.RIGHT_ARROW+" ") ; - public static final Predicate PLAIN_COMPLETION = c -> !RELAXED_COMPLETION.test(c); + public static final Predicate SNIPPET_COMPLETION = c -> c.getLabel().endsWith("Snippet"); + public static final Predicate PLAIN_COMPLETION = RELAXED_COMPLETION.negate(); public static final Predicate DEDENTED_COMPLETION = c -> c.getLabel().startsWith(Unicodes.LEFT_ARROW+" "); public static final Predicate INDENTED_COMPLETION = c -> c.getLabel().startsWith(Unicodes.RIGHT_ARROW+" "); - public static final Predicate SNIPPET_COMPLETION = c -> c.getLabel().endsWith("Snippet"); static class EditorState { String documentContents;