Dynamic CA and reconcile for network names

This commit is contained in:
Kris De Volder
2017-07-20 10:24:49 -07:00
parent 463d9aee00
commit 78743103a9
5 changed files with 110 additions and 19 deletions

View File

@@ -143,8 +143,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
YAtomicType t_url = f.yatomic("URL");
t_url.parseWith(BoshValueParsers.url("http", "https", "file"));
YAtomicType t_network_name = f.yatomic("NetworkName"); //TODO: resolve from 'cloud config' https://www.pivotaltracker.com/story/show/148712155
t_network_name.parseWith(ValueParsers.NE_STRING);
YAtomicType t_network_name = f.yenumFromDynamicValues("NetworkName", (dc) -> cloudConfigProvider.getModel(dc).getNetworkNames());
YAtomicType t_disk_type = f.yatomic("DiskType"); //TODO: resolve from 'cloud config' https://www.pivotaltracker.com/story/show/148704001
t_disk_type.parseWith(ValueParsers.NE_STRING);

View File

@@ -76,20 +76,31 @@ public class BoshCommandCloudConfigProvider implements DynamicModelProvider<Clou
.thenAnyChild()
.thenValAt("name");
protected static final YamlTraversal NETWORK_NAMES = YamlPath.EMPTY
.thenAnyChild()
.thenValAt("networks")
.thenAnyChild()
.thenValAt("name");
@Override
public CloudConfigModel getModel(DynamicSchemaContext dc) throws Exception {
String out = executeBoshCloudConfigCommand();
CloudConfigResponse response = mapper.readValue(out, CloudConfigResponse.class);
String[] blocks = response.getBlocks();
Assert.isLegal(blocks!=null);
Assert.isLegal(blocks.length==1);
String block = getCloudConfigBlock();
TextDocument doc = new TextDocument(null, LanguageId.BOSH_CLOUD_CONFIG);
doc.setText(blocks[0]);
doc.setText(block);
YamlFileAST ast = yamlParser.getAST(doc);
return new CloudConfigModel() {
@Override
public Collection<String> getVMTypes() {
return VM_TYPE_NAMES.traverseAmbiguously(ast)
return getNames(VM_TYPE_NAMES);
}
@Override
public Collection<String> getNetworkNames() {
return getNames(NETWORK_NAMES);
}
private Collection<String> getNames(YamlTraversal namesPath) {
return namesPath.traverseAmbiguously(ast)
.flatMap(nameNode -> {
String name = NodeUtil.asScalar(nameNode);
return StringUtil.hasText(name)
@@ -98,9 +109,19 @@ public class BoshCommandCloudConfigProvider implements DynamicModelProvider<Clou
})
.collect(CollectorUtil.toMultiset());
}
};
}
protected String getCloudConfigBlock() throws Exception{
String out = executeBoshCloudConfigCommand();
CloudConfigResponse response = mapper.readValue(out, CloudConfigResponse.class);
String[] blocks = response.getBlocks();
Assert.isLegal(blocks!=null);
Assert.isLegal(blocks.length==1);
return blocks[0];
}
/**
* Configure how long we wait for the command to fetch cloud config before
* raising timeout exception. (The command may block for long amounts of time

View File

@@ -17,4 +17,5 @@ import java.util.Collection;
*/
public interface CloudConfigModel {
Collection<String> getVMTypes();
Collection<String> getNetworkNames();
}

View File

@@ -125,7 +125,7 @@ public class BoshEditorTest {
//@Ignore //For now... because not passing yet.
@Test public void reconcileCfManifest() throws Exception {
Editor editor = harness.newEditorFromClasspath("/workspace/cf-deployment-manifest.yml");
cloudConfigProvider.readWith(() -> {
cloudConfigProvider.executeCommandWith(() -> {
throw new IOException("Couldn't contact the director");
});
editor.assertProblems(/*NONE*/);
@@ -718,17 +718,17 @@ public class BoshEditorTest {
"instance_groups:\n" +
"- name: foo-group\n" +
" networks:\n" +
" - name: the-network\n" +
" - name: default\n" +
" static_ips: []\n" +
" default: []\n" +
"- name: bar-group\n" +
" networks:\n" +
" - name: the-network\n" +
" - name: default\n" +
" static_ips: []\n" +
" default: []\n" +
"- name: bar-group\n" +
" networks:\n" +
" - name: the-network\n" +
" - name: default\n" +
" static_ips: []\n" +
" default: []\n" +
"releases:\n" +
@@ -877,7 +877,7 @@ public class BoshEditorTest {
}
@Test public void reconcileVMTypeWhenCloudConfigUnavailable() throws Exception {
cloudConfigProvider.readWith(() -> null);
cloudConfigProvider.executeCommandWith(() -> null);
Editor editor = harness.newEditor(
"name: foo\n" +
"instance_groups: \n" +
@@ -891,7 +891,7 @@ public class BoshEditorTest {
}
@Test public void reconcileVMTypeWhenCloudConfigThrows() throws Exception {
cloudConfigProvider.readWith(() -> { throw new TimeoutException("Reading cloud config timed out"); });
cloudConfigProvider.executeCommandWith(() -> { throw new TimeoutException("Reading cloud config timed out"); });
Editor editor = harness.newEditor(
"name: foo\n" +
"instance_groups: \n" +
@@ -905,7 +905,7 @@ public class BoshEditorTest {
}
@Test public void contentAssistShowsWarningWhenCloudConfigThrows() throws Exception {
cloudConfigProvider.readWith(() -> {
cloudConfigProvider.executeCommandWith(() -> {
throw new TimeoutException("Reading cloud config timed out");
});
Editor editor = harness.newEditor(
@@ -919,6 +919,58 @@ public class BoshEditorTest {
assertContains("Reading cloud config timed out", completion.getDocumentation());
}
@Test public void reconcileNetworkName() throws Exception {
Editor editor = harness.newEditor(
"name: my-first-deployment\n" +
"instance_groups:\n" +
"- name: my-server\n" +
" networks:\n" +
" - name: default\n" +
" - name: bogus-nw\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems("bogus-nw|unknown 'NetworkName'. Valid values are: [default]");
}
@Test public void reconcileNetworkName2() throws Exception {
cloudConfigProvider.readWith(() ->
"networks:\n" +
"- name: public-nw\n" +
"- name: local-nw"
);
Editor editor = harness.newEditor(
"name: my-first-deployment\n" +
"instance_groups:\n" +
"- name: my-server\n" +
" networks:\n" +
" - name: public-nw\n" +
" - name: local-nw\n" +
" - name: bogus-nw\n"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems("bogus-nw|unknown 'NetworkName'. Valid values are: [public-nw, local-nw]");
}
@Test public void contentAssistNetworkName() throws Exception {
cloudConfigProvider.readWith(() ->
"networks:\n" +
"- name: public-nw\n" +
"- name: local-nw"
);
Editor editor = harness.newEditor(
"name: my-first-deployment\n" +
"instance_groups:\n" +
"- name: my-server\n" +
" networks:\n" +
" - name: <*>"
);
editor.assertContextualCompletions("<*>",
"local-nw<*>", "public-nw<*>"
);
}
@Test public void gotoReleaseDefinition() throws Exception {
Editor editor = harness.newEditor(
"name: foo\n" +

View File

@@ -18,12 +18,16 @@ import org.springframework.ide.vscode.commons.util.IOUtil;
public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvider {
//TODO: use mockito to create this mock instead of a subclass?
static final String MOCK_DATA_RSRC = "/cmd-out/cloud-config.json";
private Callable<String> cloudConfigReader = () -> IOUtil.toString(BoshCommandCloudConfigProviderTest.class.getResourceAsStream(MOCK_DATA_RSRC));
private Callable<String> cloudConfigCmdExecutor = () -> IOUtil.toString(BoshCommandCloudConfigProviderTest.class.getResourceAsStream(MOCK_DATA_RSRC));
private int readCount = 0;
private Callable<String> reader = null;
/**
* Override with a 'fake' which just returns some mock data. That way we can unit-test
* without requiring a real bosh setup.
@@ -31,11 +35,25 @@ public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvide
@Override
protected String executeBoshCloudConfigCommand() throws Exception {
readCount++;
return cloudConfigReader.call();
return cloudConfigCmdExecutor.call();
}
@Override
protected String getCloudConfigBlock() throws Exception {
if (reader==null) {
return super.getCloudConfigBlock();
}
readCount++;
return reader.call();
}
public MockCloudConfigProvider executeCommandWith(Callable<String> executor) {
this.cloudConfigCmdExecutor = executor;
return this;
}
public MockCloudConfigProvider readWith(Callable<String> reader) {
this.cloudConfigReader = reader;
this.reader = reader;
return this;
}