Merge branch 'master' of https://github.com/spring-projects/sts4
This commit is contained in:
@@ -79,6 +79,14 @@ resources:
|
||||
secret_access_key: {{s3_secretkey}}
|
||||
region_name: {{s3_region}}
|
||||
regexp: sts4/vscode-extensions/snapshots/vscode-manifest-yaml-(.*).vsix
|
||||
- name: s3-manifest-yaml-fatjar-snapshot
|
||||
type: s3
|
||||
source:
|
||||
bucket: {{s3_bucket}}
|
||||
access_key_id: {{s3_accesskey}}
|
||||
secret_access_key: {{s3_secretkey}}
|
||||
region_name: {{s3_region}}
|
||||
regexp: sts4/fatjars/snapshots/manifest-yaml-language-server-(.*).jar
|
||||
- name: snapshot-website
|
||||
type: s3-multi
|
||||
source:
|
||||
@@ -232,10 +240,15 @@ jobs:
|
||||
params:
|
||||
text: |
|
||||
Concourse ${BUILD_PIPELINE_NAME}/${BUILD_JOB_NAME}/${BUILD_NAME} has failed!
|
||||
- put: s3-manifest-yaml-vsix-snapshot
|
||||
params:
|
||||
file: out/vscode-manifest-yaml-*.vsix
|
||||
acl: public-read
|
||||
- aggregate:
|
||||
- put: s3-manifest-yaml-vsix-snapshot
|
||||
params:
|
||||
file: out/vscode-manifest-yaml-*.vsix
|
||||
acl: public-read
|
||||
- put: s3-manifest-yaml-fatjar-snapshot
|
||||
params:
|
||||
file: out/*.jar
|
||||
acl: public-read
|
||||
- name: trigger-rc-build
|
||||
serial: true
|
||||
plan:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
set -e
|
||||
workdir=`pwd`
|
||||
sources=$workdir/sts4/vscode-extensions/$extension_id
|
||||
server_id=${extension_id#vscode-}
|
||||
|
||||
if [ -d "maven-cache" ]; then
|
||||
echo "Prepopulating maven cache"
|
||||
@@ -48,3 +49,4 @@ if [ "$dist_type" == release ]; then
|
||||
fi
|
||||
|
||||
cp *.vsix $workdir/out
|
||||
cp $workdir/sts4/headless-services/${server_id}-language-server/target/*.jar $workdir/out/${server_id}-language-server-${base_version}-${timestamp}.jar
|
||||
|
||||
@@ -103,4 +103,5 @@ public class TypedProperty implements YTypedProperty {
|
||||
public Deprecation getDeprecation() {
|
||||
return deprecation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
eclipse.preferences.version=1
|
||||
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
|
||||
sp_cleanup.add_default_serial_version_id=true
|
||||
sp_cleanup.add_generated_serial_version_id=false
|
||||
sp_cleanup.add_missing_annotations=true
|
||||
sp_cleanup.add_missing_deprecated_annotations=true
|
||||
sp_cleanup.add_missing_methods=false
|
||||
sp_cleanup.add_missing_nls_tags=false
|
||||
sp_cleanup.add_missing_override_annotations=true
|
||||
sp_cleanup.add_missing_override_annotations_interface_methods=true
|
||||
sp_cleanup.add_serial_version_id=false
|
||||
sp_cleanup.always_use_blocks=true
|
||||
sp_cleanup.always_use_parentheses_in_expressions=false
|
||||
sp_cleanup.always_use_this_for_non_static_field_access=false
|
||||
sp_cleanup.always_use_this_for_non_static_method_access=false
|
||||
sp_cleanup.convert_functional_interfaces=false
|
||||
sp_cleanup.convert_to_enhanced_for_loop=false
|
||||
sp_cleanup.correct_indentation=false
|
||||
sp_cleanup.format_source_code=false
|
||||
sp_cleanup.format_source_code_changes_only=false
|
||||
sp_cleanup.insert_inferred_type_arguments=false
|
||||
sp_cleanup.make_local_variable_final=true
|
||||
sp_cleanup.make_parameters_final=false
|
||||
sp_cleanup.make_private_fields_final=true
|
||||
sp_cleanup.make_type_abstract_if_missing_method=false
|
||||
sp_cleanup.make_variable_declarations_final=false
|
||||
sp_cleanup.never_use_blocks=false
|
||||
sp_cleanup.never_use_parentheses_in_expressions=true
|
||||
sp_cleanup.on_save_use_additional_actions=true
|
||||
sp_cleanup.organize_imports=true
|
||||
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
|
||||
sp_cleanup.remove_private_constructors=true
|
||||
sp_cleanup.remove_redundant_type_arguments=false
|
||||
sp_cleanup.remove_trailing_whitespaces=true
|
||||
sp_cleanup.remove_trailing_whitespaces_all=true
|
||||
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
|
||||
sp_cleanup.remove_unnecessary_casts=true
|
||||
sp_cleanup.remove_unnecessary_nls_tags=false
|
||||
sp_cleanup.remove_unused_imports=false
|
||||
sp_cleanup.remove_unused_local_variables=false
|
||||
sp_cleanup.remove_unused_private_fields=true
|
||||
sp_cleanup.remove_unused_private_members=false
|
||||
sp_cleanup.remove_unused_private_methods=true
|
||||
sp_cleanup.remove_unused_private_types=true
|
||||
sp_cleanup.sort_members=false
|
||||
sp_cleanup.sort_members_all=false
|
||||
sp_cleanup.use_anonymous_class_creation=false
|
||||
sp_cleanup.use_blocks=false
|
||||
sp_cleanup.use_blocks_only_for_return_and_throw=false
|
||||
sp_cleanup.use_lambda=true
|
||||
sp_cleanup.use_parentheses_in_expressions=false
|
||||
sp_cleanup.use_this_for_non_static_field_access=false
|
||||
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
|
||||
sp_cleanup.use_this_for_non_static_method_access=false
|
||||
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
|
||||
@@ -45,6 +45,18 @@
|
||||
<version>${dependencies.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>${mockito-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.MISSING_PROPERTY;
|
||||
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.problem;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
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.constraints.Constraint;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
|
||||
public class BoshConstraints {
|
||||
|
||||
public static final Constraint SHA1_REQUIRED_FOR_HTTP_URL = new Constraint() {
|
||||
|
||||
@Override
|
||||
public void verify(DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) {
|
||||
NodeTuple urlProp = NodeUtil.getPropertyTuple(node, "url");
|
||||
if (urlProp!=null) {
|
||||
String url = NodeUtil.asScalar(urlProp.getValueNode());
|
||||
if (url!=null && url.startsWith("http")) {
|
||||
Node sha1 = NodeUtil.getProperty(node, "sha1");
|
||||
if (sha1==null) {
|
||||
problems.accept(problem(MISSING_PROPERTY, "'sha1' is required when the 'url' is http(s)", urlProp.getKeyNode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -10,30 +10,52 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
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.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.ValueParsers;
|
||||
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.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 org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraints;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
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");
|
||||
private ImmutableList<YType> definitionTypes = null;
|
||||
//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);
|
||||
@@ -49,45 +71,180 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
|
||||
public final YType t_strictly_pos_integer = f.yatomic("Strictly Positive Integer")
|
||||
.parseWith(ValueParsers.integerAtLeast(1));
|
||||
public final YType t_uuid = f.yatomic("UUID").parseWith(UUID::fromString);
|
||||
public final YType t_integer_or_range = f.yatomic("Integer or Range")
|
||||
.parseWith(BoshValueParsers.INTEGER_OR_RANGE);
|
||||
private YType t_instance_group_name_def;
|
||||
private YType t_stemcell_alias_def;
|
||||
private YType t_stemcell_alias_ref;
|
||||
private YType t_release_name_def;
|
||||
private YType t_release_name_ref;
|
||||
private YType t_var_name_def;
|
||||
private final ASTTypeCache astTypes;
|
||||
private DynamicModelProvider<CloudConfigModel> cloudConfigProvider;
|
||||
|
||||
public BoshDeploymentManifestSchema() {
|
||||
public BoshDeploymentManifestSchema(ASTTypeCache astTypes, DynamicModelProvider<CloudConfigModel> cloudConfigProvider) {
|
||||
this.astTypes = astTypes;
|
||||
this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider);
|
||||
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)
|
||||
.isRequired(true);
|
||||
|
||||
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."
|
||||
);
|
||||
|
||||
t_instance_group_name_def = f.yatomic("InstanceGroupName")
|
||||
.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
t_stemcell_alias_def = f.yatomic("StemcellAlias")
|
||||
.parseWith(ValueParsers.NE_STRING);
|
||||
t_stemcell_alias_ref = f.yenumFromDynamicValues("StemcellAlias", (dc) -> astTypes.getDefinedNames(dc, t_stemcell_alias_def));
|
||||
|
||||
t_release_name_def = f.yatomic("ReleaseName")
|
||||
.parseWith(ValueParsers.NE_STRING);
|
||||
t_release_name_ref = f.yenumFromDynamicValues("ReleaseName", (dc) -> astTypes.getDefinedNames(dc, t_release_name_def));
|
||||
|
||||
t_var_name_def = f.yatomic("VariableName")
|
||||
.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
YAtomicType t_ip_address = f.yatomic("IPAddress"); //TODO: some kind of checking?
|
||||
t_ip_address.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
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_disk_type = f.yatomic("DiskType"); //TODO: resolve from 'cloud config' https://www.pivotaltracker.com/story/show/148704001
|
||||
t_disk_type.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
YAtomicType t_vm_extension = f.yatomic("VMExtension"); //TODO: resolve dynamically from 'cloud config' ? https://www.pivotaltracker.com/story/show/148703877
|
||||
t_vm_extension.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
YAtomicType t_vm_type = f.yenumFromDynamicValues("VMType", (dc) -> cloudConfigProvider.getModel(dc).getVMTypes());
|
||||
|
||||
YAtomicType t_az = f.yatomic("AvailabilityZone"); //TODO: resolve dynamically from 'cloud config': https://www.pivotaltracker.com/story/show/148704481
|
||||
t_az.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
YBeanType t_network = f.ybean("Network");
|
||||
addProp(t_network, "name", t_network_name).isRequired(true);
|
||||
addProp(t_network, "static_ips", f.yseq(t_ip_address));
|
||||
addProp(t_network, "default", f.yseq(t_ne_string)); //TODO: Can we determine the set of valid values? How?
|
||||
|
||||
YBeanType t_instance_group_env = f.ybean("InstanceGroupEnv");
|
||||
addProp(t_instance_group_env, "bosh", t_params);
|
||||
addProp(t_instance_group_env, "password", t_ne_string);
|
||||
|
||||
YAtomicType t_version = f.yatomic("Version");
|
||||
t_version.addHints("latest");
|
||||
t_version.parseWith(ValueParsers.NE_STRING);
|
||||
|
||||
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(t_release, "name", t_release_name_def).isPrimary(true);
|
||||
addProp(t_release, "version", t_version);
|
||||
//TODO: the checking here is just 'my best guess'. Unclarity remains:
|
||||
// See: https://github.com/cloudfoundry/docs-bosh/issues/330
|
||||
addProp(t_release, "url", t_url);
|
||||
addProp(t_release, "sha1", t_ne_string);
|
||||
addProp(v2Schema, "releases", f.yseq(t_release)).isRequired(true);
|
||||
t_release.require(Constraints.requireAtLeastOneOf("url", "version"));
|
||||
// ^^^^^^^ allthough docs seem to imply you shouldn't
|
||||
// define both url and version it seems that bosh tolerates it.
|
||||
t_release.require(BoshConstraints.SHA1_REQUIRED_FOR_HTTP_URL);
|
||||
|
||||
YBeanType t_stemcell = f.ybean("Stemcell");
|
||||
addProp(t_stemcell, "alias", t_ne_string).isRequired(true);
|
||||
addProp(t_stemcell, "alias", t_stemcell_alias_def).isRequired(true);
|
||||
addProp(t_stemcell, "version", t_ne_string).isRequired(true);
|
||||
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);
|
||||
|
||||
YType t_update = t_params; //TODO: https://www.pivotaltracker.com/story/show/148627121
|
||||
addProp(TOPLEVEL_TYPE, "update", t_update).isRequired(true);
|
||||
|
||||
YType t_instance_group = t_params; //TODO: https://www.pivotaltracker.com/story/show/148627211
|
||||
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");
|
||||
|
||||
YType t_variable = t_params; //TODO: https://www.pivotaltracker.com/story/show/148627441
|
||||
addProp(TOPLEVEL_TYPE, "variables", f.yseq(t_variable));
|
||||
YBeanType t_update = f.ybean("Update");
|
||||
addProp(t_update, "canaries", t_strictly_pos_integer).isRequired(true);
|
||||
addProp(t_update, "max_in_flight", t_pos_integer).isRequired(true);
|
||||
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(v2Schema, "update", t_update).isRequired(true);
|
||||
|
||||
addProp(TOPLEVEL_TYPE, "tags", t_params);
|
||||
|
||||
YBeanType t_job = f.ybean("Job");
|
||||
addProp(t_job, "name", t_ne_string).isRequired(true);
|
||||
addProp(t_job, "release", t_release_name_ref).isRequired(true);
|
||||
addProp(t_job, "consumes", t_params);
|
||||
addProp(t_job, "provides", t_params);
|
||||
addProp(t_job, "properties", t_params);
|
||||
|
||||
YBeanType t_instance_group = f.ybean("InstanceGroup");
|
||||
addProp(t_instance_group, "name", t_instance_group_name_def).isPrimary(true);
|
||||
addProp(t_instance_group, "azs", f.yseq(t_az)).isRequired(true);
|
||||
addProp(t_instance_group, "instances", t_pos_integer).isRequired(true); //Strictly positive? Or zero is okay?
|
||||
addProp(t_instance_group, "jobs", f.yseq(t_job)).isRequired(true);
|
||||
addProp(t_instance_group, "vm_type", t_vm_type).isRequired(true);
|
||||
addProp(t_instance_group, "vm_extensions", f.yseq(t_vm_extension));
|
||||
addProp(t_instance_group, "stemcell", t_stemcell_alias_ref).isRequired(true);
|
||||
addProp(t_instance_group, "persistent_disk_type", t_disk_type);
|
||||
addProp(t_instance_group, "networks", f.yseq(t_network)).isRequired(true);
|
||||
YType t_update_override = f.ybean("UpdateOverrides", t_update.getProperties()
|
||||
.stream()
|
||||
.map((YTypedProperty prop) ->
|
||||
f.yprop(prop).isRequired(false)
|
||||
)
|
||||
.toArray(sz -> new YTypedProperty[sz])
|
||||
);
|
||||
addProp(t_instance_group, "update", t_update_override);
|
||||
YType t_migration = t_params; //TODO: https://www.pivotaltracker.com/story/show/148712595
|
||||
addProp(t_instance_group, "migrated_from", f.yseq(t_migration));
|
||||
addProp(t_instance_group, "lifecycle", f.yenum("WorkloadType", "service", "errand"));
|
||||
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(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_var_name_def).isPrimary(true);
|
||||
addProp(t_variable, "type", f.yenum("VariableType", "certificate", "password", "rsa", "ssh")).isRequired(true);
|
||||
addProp(t_variable, "options", t_params);
|
||||
addProp(v2Schema, "variables", f.yseq(t_variable));
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
for (YType defType : getDefinitionTypes()) {
|
||||
v2Schema.require(Constraints.uniqueDefinition(this.astTypes, defType, YamlSchemaProblems.problemType("BOSH_DUPLICATE_"+defType)));
|
||||
}
|
||||
return v2Schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,13 +256,13 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
|
||||
public YTypeUtil getTypeUtil() {
|
||||
return TYPE_UTIL;
|
||||
}
|
||||
|
||||
|
||||
private YTypedPropertyImpl prop(AbstractType beanType, String name, YType type) {
|
||||
YTypedPropertyImpl prop = f.yprop(name, type);
|
||||
prop.setDescriptionProvider(descriptionFor(beanType, name));
|
||||
return prop;
|
||||
}
|
||||
|
||||
|
||||
public static Renderable descriptionFor(YType owner, String propName) {
|
||||
String typeName = owner.toString();
|
||||
return Renderables.fromClasspath(BoshDeploymentManifestSchema.class, "/desc/"+typeName+"/"+propName);
|
||||
@@ -121,4 +278,16 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
|
||||
return p;
|
||||
}
|
||||
|
||||
public Collection<YType> getDefinitionTypes() {
|
||||
if (definitionTypes==null) {
|
||||
definitionTypes = ImmutableList.of(
|
||||
t_instance_group_name_def,
|
||||
t_stemcell_alias_def,
|
||||
t_release_name_def,
|
||||
t_var_name_def
|
||||
);
|
||||
}
|
||||
return definitionTypes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
|
||||
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter.LazyCompletionResolver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
|
||||
@@ -28,8 +30,9 @@ import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngi
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlSymbolHandler;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
|
||||
import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvider;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
@@ -39,11 +42,12 @@ public class BoshLanguageServer extends SimpleLanguageServer {
|
||||
private final LazyCompletionResolver completionResolver = new LazyCompletionResolver(); //Set to null to disable lazy resolving
|
||||
private final VscodeCompletionEngineAdapter completionEngine;
|
||||
|
||||
public BoshLanguageServer() {
|
||||
public BoshLanguageServer(DynamicModelProvider<CloudConfigModel> cloudConfigProvider) {
|
||||
super("vscode-bosh");
|
||||
YamlASTProvider parser = new YamlParser(yaml);
|
||||
SimpleTextDocumentService documents = getTextDocumentService();
|
||||
YamlSchema schema = new BoshDeploymentManifestSchema();
|
||||
ASTTypeCache astTypeCache = new ASTTypeCache();
|
||||
BoshDeploymentManifestSchema schema = new BoshDeploymentManifestSchema(astTypeCache, cloudConfigProvider);
|
||||
|
||||
YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT;
|
||||
YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema);
|
||||
@@ -53,7 +57,9 @@ public class BoshLanguageServer extends SimpleLanguageServer {
|
||||
HoverInfoProvider infoProvider = new YamlHoverInfoProvider(parser, structureProvider, contextProvider);
|
||||
VscodeHoverEngine hoverEngine = new VscodeHoverEngineAdapter(this, infoProvider);
|
||||
YamlQuickfixes quickfixes = new YamlQuickfixes(getQuickfixRegistry(), getTextDocumentService(), structureProvider);
|
||||
IReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema, quickfixes);
|
||||
YamlSchemaBasedReconcileEngine engine = new YamlSchemaBasedReconcileEngine(parser, schema, quickfixes);
|
||||
engine.setTypeCollector(astTypeCache);
|
||||
documents.onDocumentSymbol(new TypeBasedYamlSymbolHandler(documents, astTypeCache, schema.getDefinitionTypes()));
|
||||
|
||||
documents.onDidChangeContent(params -> {
|
||||
validateOnDocumentChange(engine, params.getDocument());
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class BoshValueParsers {
|
||||
|
||||
public static ValueParser url(String... _schemes) {
|
||||
return new ValueParser() {
|
||||
|
||||
private final ImmutableList<String> validSchemes = ImmutableList.copyOf(_schemes);
|
||||
|
||||
@Override
|
||||
public Object parse(String s) throws Exception {
|
||||
URI uri = new URI(s);
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme==null) {
|
||||
throw new ValueParseException(message());
|
||||
} else if (!validSchemes.contains(scheme.toLowerCase())) {
|
||||
int start = s.indexOf(scheme);
|
||||
if (start>=0) {
|
||||
int end = start + scheme.length();
|
||||
throw new ValueParseException(message(), start, end);
|
||||
} else {
|
||||
// Trouble finding exact location of underlined region so underline whole url
|
||||
throw new ValueParseException(message());
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
private String message() {
|
||||
return "Url scheme must be one of "+validSchemes;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final ValueParser INTEGER_OR_RANGE = new ValueParser() {
|
||||
|
||||
private int findDash(String s) throws ValueParseException {
|
||||
int firstDash = s.indexOf('-');
|
||||
if (firstDash<0) {
|
||||
return firstDash; //no dash... and that's okay!
|
||||
}
|
||||
int secondDash = s.indexOf('-', firstDash+1);
|
||||
if (secondDash>=0) {
|
||||
//Only one dash is expected!
|
||||
throw new ValueParseException("Should be either a Integer, or a range (of the form '<integer>-<integer>')");
|
||||
}
|
||||
return firstDash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object parse(String s) throws Exception {
|
||||
int dash = findDash(s);
|
||||
if (dash>=0) {
|
||||
int low, high;
|
||||
//range
|
||||
try {
|
||||
low = Integer.parseInt(s.substring(0, dash));
|
||||
} catch (Exception e) {
|
||||
throw new ValueParseException("Should be a Integer", 0, dash);
|
||||
}
|
||||
try {
|
||||
high = Integer.parseInt(s.substring(dash+1));
|
||||
} catch (Exception e) {
|
||||
throw new ValueParseException("Should be a Integer", dash+1, s.length());
|
||||
}
|
||||
if (low>high) {
|
||||
throw new ValueParseException(low + " should be smaller than "+high);
|
||||
}
|
||||
} else {
|
||||
//integer
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (Exception e) {
|
||||
throw new ValueParseException("Should be either a Integer, or a range (of the form '<integer>-<integer>')");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -8,16 +8,15 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
LaunguageServerApp.start(() -> new BoshLanguageServer());
|
||||
LaunguageServerApp.start(() -> new BoshLanguageServer(new BoshCommandCloudConfigProvider()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*******************************************************************************
|
||||
* 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.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ExternalCommand;
|
||||
import org.springframework.ide.vscode.commons.util.ExternalProcess;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlTraversal;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Concrete implementation of {@link CloudConfigProvider} that runs `bosh cloud-config` command
|
||||
* with bosh cli and parses the output.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class BoshCommandCloudConfigProvider implements DynamicModelProvider<CloudConfigModel> {
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
final YamlParser yamlParser;
|
||||
|
||||
private Duration CMD_TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
public BoshCommandCloudConfigProvider() {
|
||||
Representer representer = new Representer();
|
||||
representer.getPropertyUtils().setSkipMissingProperties(true);
|
||||
yamlParser = new YamlParser(new Yaml());
|
||||
}
|
||||
|
||||
/**
|
||||
* For deserializing the output from bosh cloud-config command.
|
||||
*/
|
||||
public static class CloudConfigResponse {
|
||||
private String[] blocks;
|
||||
|
||||
@JsonProperty("Blocks")
|
||||
public String[] getBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public void setBlocks(String[] blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
}
|
||||
|
||||
YamlTraversal VM_TYPE_NAMES = YamlPath.EMPTY
|
||||
.thenAnyChild()
|
||||
.thenValAt("vm_types")
|
||||
.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);
|
||||
TextDocument doc = new TextDocument(null, LanguageId.BOSH_CLOUD_CONFIG);
|
||||
doc.setText(blocks[0]);
|
||||
YamlFileAST ast = yamlParser.getAST(doc);
|
||||
return new CloudConfigModel() {
|
||||
@Override
|
||||
public Collection<String> getVMTypes() {
|
||||
return VM_TYPE_NAMES.traverseAmbiguously(ast)
|
||||
.flatMap(nameNode -> {
|
||||
String name = NodeUtil.asScalar(nameNode);
|
||||
return StringUtil.hasText(name)
|
||||
? Stream.of(name)
|
||||
: Stream.empty();
|
||||
})
|
||||
.collect(CollectorUtil.toMultiset());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* of the director is unreachable on the network).
|
||||
*/
|
||||
public void setCommandTimeout(Duration duration) {
|
||||
this.CMD_TIMEOUT = duration;
|
||||
}
|
||||
|
||||
protected String executeBoshCloudConfigCommand() throws Exception {
|
||||
ExternalCommand command = new ExternalCommand("bosh", "cloud-config", "--json");
|
||||
ExternalProcess process = new ExternalProcess(new File(".").getAbsoluteFile(), command, true, CMD_TIMEOUT);
|
||||
System.out.println("executeBoshCloudConfigCommand: "+process);
|
||||
String out = process.getOut();
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*******************************************************************************
|
||||
* 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.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
/**
|
||||
* Wraps around a {@link DynamicModelProvider} to add a cache.
|
||||
*/
|
||||
public class CachingModelProvider<T> implements DynamicModelProvider<T> {
|
||||
|
||||
/**
|
||||
* Special key to use when the actual key is null (because guava cache doesn't
|
||||
* like null keys).
|
||||
*/
|
||||
private static final Object NULL_KEY = new Object();
|
||||
|
||||
private long timeout = 15;
|
||||
private TimeUnit timeoutUnit = TimeUnit.SECONDS;
|
||||
private Cache<Object, T> cache = createCache();
|
||||
|
||||
private final DynamicModelProvider<T> delegate;
|
||||
|
||||
public CachingModelProvider(DynamicModelProvider<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to determine the caching key, given the current {@link DynamicSchemaContext}.
|
||||
* <p>
|
||||
* The default keyGetter ignores the context and just returns the same object all the time. This
|
||||
* results in a cache that only keeps a single value (since there's only a single key ever used
|
||||
* to store / find cache entries.
|
||||
*/
|
||||
private Function<DynamicSchemaContext, Object> keyGetter = (dc) -> "WHATEVER";
|
||||
|
||||
protected Cache<Object, T> createCache() {
|
||||
return CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(timeout, timeoutUnit)
|
||||
.build();
|
||||
}
|
||||
|
||||
public CachingModelProvider<T> setTimeout(long timeout, TimeUnit unit) {
|
||||
this.timeout = timeout;
|
||||
this.timeoutUnit = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getModel(DynamicSchemaContext dc) throws Exception {
|
||||
Object key = keyGetter.apply(dc);
|
||||
if (key==null) {
|
||||
//guava cache doesn't like null key
|
||||
key = NULL_KEY;
|
||||
}
|
||||
return cache.get(key, () -> delegate.getModel(dc));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*******************************************************************************
|
||||
* 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.Collection;
|
||||
|
||||
/**
|
||||
* Represents CloudConfig information as might be retrieved from bosh director.
|
||||
*/
|
||||
public interface CloudConfigModel {
|
||||
Collection<String> getVMTypes();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.springframework.ide.vscode.bosh.models;
|
||||
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
|
||||
/**
|
||||
* Responsible for somehow obtaining {@link CloudConfigModel} relative to a {@link DynamicSchemaContext}
|
||||
*/
|
||||
public interface DynamicModelProvider<T> {
|
||||
T getModel(DynamicSchemaContext dc) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. List of AZs associated with this instance group (should only be used when using [first class AZs](https://bosh.io/docs/azs.html) ). Example: `[z1, z2]`.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specifies advanced BOSH Agent configuration for each instance in the group.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. The number of instances in this group. Each instance is a VM.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Specifies the name and release of jobs that will be installed on each instance.
|
||||
@@ -0,0 +1 @@
|
||||
Specifies the kind of workload the instance group represents. Valid values are `service` and `errand`; defaults to `service`. A `service` runs indefinitely and restarts if it fails. An `errand` starts with a manual trigger and does not restart if it fails.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specific migration settings for this instance group. Use this to [rename and/or migrate instance groups](https://bosh.io/docs/migrated-from.html).
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. A unique name used to identify and reference the instance group.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Specifies the networks this instance requires.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. A valid disk type name from the cloud config. [Read more about persistent disks](https://bosh.io/docs/persistent-disks.html)
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specifies instance group properties. Deprecated in favor of job level properties and links.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. A valid stemcell alias from the Stemcells Block.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specific update settings for this instance group. Use this to override [global job update settings](https://bosh.io/docs/manifest-v2.html#update) on a per-instance-group basis.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. A valid list of VM extension names from the cloud config.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. A valid VM type name from the cloud config.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Crypted password for `vcap/root` user (will be placed into `/etc/shadow` on Linux).
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Links consumed by the job. [Read more about link configuration](https://bosh.io/docs/links.html#deployment)
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. The job name.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specifies job properties. Properties allow BOSH to configure jobs to a specific environment. `properties` defined in a Job block are accessible only to that job. Only properties specified here will be provided to the job.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Links provided by the job. [Read more about link configuration](https://bosh.io/docs/links.html#deployment)
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. The release where the job exists
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specifies which network components (DNS, Gateway) BOSH populates by default from this network. This property is required if more than one network is specified.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. A valid network name from the cloud config.
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Array of IP addresses reserved for the instances on the network.
|
||||
@@ -0,0 +1 @@
|
||||
The SHA1 of the release tarball. SHA1 is only required when using HTTP(s) URLs.
|
||||
@@ -0,0 +1 @@
|
||||
URL of the release to use. URL may use the file protocol (`file://`) or HTTP(s) (`http(s)://`). File URLs can be absolute or relative to the current directory of `bosh-init` execution.
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. The number of [canary](https://bosh.io/docs/terminology.html#canary) instances.
|
||||
@@ -0,0 +1,6 @@
|
||||
*Required*. Only applies to monit start operation.
|
||||
|
||||
- If the `canary_watch_time` is an integer, the Director sleeps for that many milliseconds, then checks whether the canary instances are healthy.
|
||||
- If the `canary_watch_time` is a range (low-high), the Director:
|
||||
- Waits for `low` milliseconds
|
||||
- Waits until instances are healthy or `high` milliseconds have passed since instances started updating
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. The maximum number of non-canary instances to update in parallel.
|
||||
@@ -0,0 +1 @@
|
||||
If disabled (set to `false`), instance groups will be deployed in parallel, otherwise - sequentially. Instances within a group will still follow `canary` and `max_in_flight` configuration. Defaults to `true`.
|
||||
@@ -0,0 +1,6 @@
|
||||
*Required*. Only applies to monit start operation.
|
||||
|
||||
- If the `update_watch_time` is an integer, the Director sleeps for that many milliseconds, then checks whether the instances are healthy.
|
||||
- If the `update_watch_time` is a range (low-high), the Director:
|
||||
- Waits for `low` milliseconds
|
||||
- Waits until instances are healthy or `high` milliseconds have passed since instances started updating
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Unique name used to identify a variable. Example: `admin_password`
|
||||
@@ -0,0 +1 @@
|
||||
*Optional*. Specifies generation options used for generating variable value if variable is not found. Example: `{is_ca: true, common_name: some-ca}`
|
||||
@@ -0,0 +1 @@
|
||||
*Required*. Type of a variable. Currently supported variable types are `certificate`, `password`, `rsa`, and `ssh`. Example: `password`.
|
||||
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
|
||||
public class BoshCommandCloudConfigProviderTest {
|
||||
|
||||
public final MockCloudConfigProvider mockProvider = new MockCloudConfigProvider();
|
||||
|
||||
// For local testing only... in CI builds we don't have the means to use a real bosh director and cli.
|
||||
// private BoshCommandCloudConfigProvider realProvider = new BoshCommandCloudConfigProvider();
|
||||
|
||||
@Test public void getVMTypes() throws Exception {
|
||||
BoshCommandCloudConfigProvider provider = mockProvider;
|
||||
DynamicSchemaContext dc = Mockito.mock(DynamicSchemaContext.class);
|
||||
CloudConfigModel cloudConfig = provider.getModel(dc);
|
||||
|
||||
assertEquals(ImmutableMultiset.of("default", "large"), cloudConfig.getVMTypes());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,923 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.Editor.PLAIN_COMPLETION;
|
||||
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
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;
|
||||
|
||||
|
||||
public class BoshEditorTest {
|
||||
|
||||
LanguageServerHarness harness;
|
||||
|
||||
private MockCloudConfigProvider cloudConfigProvider = new MockCloudConfigProvider();
|
||||
|
||||
@Before public void setup() throws Exception {
|
||||
harness = new LanguageServerHarness(() -> {
|
||||
return new BoshLanguageServer(cloudConfigProvider)
|
||||
.setMaxCompletions(100);
|
||||
},
|
||||
LanguageId.BOSH_DEPLOYMENT
|
||||
);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test public void toplevelV2PropertyNamesKnown() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: some-name\n" +
|
||||
"director_uuid: cf8dc1fc-9c42-4ffc-96f1-fbad983a6ce6\n" +
|
||||
"releases:\n" +
|
||||
"- name: redis\n" +
|
||||
" version: 12\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: default\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: 3421.11\n" +
|
||||
"update:\n" +
|
||||
" canaries: 1\n" +
|
||||
" max_in_flight: 10\n" +
|
||||
" canary_watch_time: 1000-30000\n" +
|
||||
" update_watch_time: 1000-30000\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: redis-master\n" +
|
||||
" instances: 1\n" +
|
||||
" azs: [z1, z2]\n" +
|
||||
" jobs:\n" +
|
||||
" - name: redis-server\n" +
|
||||
" release: redis\n" +
|
||||
" properties:\n" +
|
||||
" port: 3606\n" +
|
||||
" vm_type: large\n" +
|
||||
" vm_extensions: [public-lbs]\n" +
|
||||
" stemcell: default\n" +
|
||||
" persistent_disk_type: medium\n" +
|
||||
" networks:\n" +
|
||||
" - name: default\n" +
|
||||
"- name: redis-slave\n" +
|
||||
" instances: 2\n" +
|
||||
" azs: [z1, z2]\n" +
|
||||
" jobs:\n" +
|
||||
" - name: redis-server\n" +
|
||||
" release: redis\n" +
|
||||
" properties: {}\n" +
|
||||
" vm_type: large\n" +
|
||||
" stemcell: default\n" +
|
||||
" persistent_disk_type: large\n" +
|
||||
" networks:\n" +
|
||||
" - name: default\n" +
|
||||
"variables:\n" +
|
||||
"- name: admin_password\n" +
|
||||
" type: password\n" +
|
||||
"- name: default_ca\n" +
|
||||
" type: certificate\n" +
|
||||
" options:\n" +
|
||||
" is_ca: true\n" +
|
||||
" common_name: some-ca\n" +
|
||||
"- name: director_ssl\n" +
|
||||
" type: certificate\n" +
|
||||
" options:\n" +
|
||||
" ca: default_ca\n" +
|
||||
" common_name: cc.cf.internal\n" +
|
||||
" alternative_names: [cc.cf.internal]\n" +
|
||||
"properties:\n" +
|
||||
" a-property: the-value\n" +
|
||||
"tags:\n" +
|
||||
" project: cf\n" +
|
||||
"blah: hoooo\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"director_uuid|bosh v2 CLI no longer checks or requires",
|
||||
"properties|Deprecated in favor of job level properties and links",
|
||||
"blah|Unknown property"
|
||||
);
|
||||
|
||||
editor.assertHoverContains("name", "The name of the deployment");
|
||||
editor.assertHoverContains("director_uuid", "This string must match the UUID of the currently targeted Director");
|
||||
editor.assertHoverContains("releases", "The name and version of each release in the deployment");
|
||||
editor.assertHoverContains("stemcells", "The name and version of each stemcell");
|
||||
editor.assertHoverContains("update", "This specifies instance update properties");
|
||||
editor.assertHoverContains("instance_groups", "Specifies the mapping between release [jobs](https://bosh.io/docs/terminology.html#job) and instance groups.");
|
||||
editor.assertHoverContains("properties", 3, "Describes global properties. Deprecated");
|
||||
editor.assertHoverContains("variables", "Describes variables");
|
||||
editor.assertHoverContains("tags", "Specifies key value pairs to be sent to the CPI for VM tagging");
|
||||
}
|
||||
|
||||
//@Ignore //For now... because not passing yet.
|
||||
@Test public void reconcileCfManifest() throws Exception {
|
||||
Editor editor = harness.newEditorFromClasspath("/workspace/cf-deployment-manifest.yml");
|
||||
cloudConfigProvider.readWith(() -> {
|
||||
throw new IOException("Couldn't contact the director");
|
||||
});
|
||||
editor.assertProblems(/*NONE*/);
|
||||
}
|
||||
|
||||
@Test public void toplevelPropertyCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"<*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
"name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"name: blah\n" +
|
||||
"<*>"
|
||||
);
|
||||
|
||||
editor.assertCompletions(
|
||||
"name: blah\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"releases:\n" +
|
||||
"- name: <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"stemcells:\n- <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"tags:\n <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"update:\n <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"variables:\n" +
|
||||
"- name: <*>"
|
||||
// Below completions are suppressed because they are deprecated
|
||||
// , // ============
|
||||
// "name: blah\n" +
|
||||
// "director_uuid: <*>"
|
||||
// , // ============
|
||||
// "name: blah\n" +
|
||||
// "properties:\n <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void stemcellCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"stemcells:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
"stemcells:\n" +
|
||||
"- alias: <*>"
|
||||
, // ==========
|
||||
"stemcells:\n" +
|
||||
"- name: <*>"
|
||||
, // ==========
|
||||
"stemcells:\n" +
|
||||
"- os: <*>"
|
||||
, // ==========
|
||||
"stemcells:\n" +
|
||||
"- version: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"stemcells:\n" +
|
||||
"- alias<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void stemcellReconciling() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"stemcells:\n" +
|
||||
"- {}"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"-|One of [name, os] is required",
|
||||
"-|[alias, version] are required",
|
||||
"}|[instance_groups, name, releases, update] are required"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void releasesBlockCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertCompletions(
|
||||
"releases:\n" +
|
||||
"- name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: foo\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertCompletions(PLAIN_COMPLETION,
|
||||
"releases:\n" +
|
||||
"- name: foo\n" +
|
||||
" sha1: <*>"
|
||||
, // ============
|
||||
"releases:\n" +
|
||||
"- name: foo\n" +
|
||||
" url: <*>"
|
||||
, // ============
|
||||
"releases:\n" +
|
||||
"- name: foo\n" +
|
||||
" version: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void releasesAdvancedValidations() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: some-release\n" +
|
||||
" url: https://my.releases.com/funky.tar.gz\n" +
|
||||
"- name: other-relase\n" +
|
||||
" url: file:///root/releases/a-nice-file.tar.gz\n" +
|
||||
"- name: bad-url\n" +
|
||||
" url: proto://something.com\n" +
|
||||
"#x"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"url|'sha1' is required when the 'url' is http(s)",
|
||||
"proto|Url scheme must be one of [http, https, file]",
|
||||
"x|are required"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void releasesBlockPropertyReconcileAndHovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: some-release\n" +
|
||||
" version: some-version\n" +
|
||||
" url: https://my.releases.com/funky.tar.gz\n" +
|
||||
" sha1: 440248a31253296b1626ad52886e58900730f32e\n" +
|
||||
" woot: dunno\n"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(
|
||||
"woot|Unknown property"
|
||||
);
|
||||
|
||||
editor.assertHoverContains("name", "Name of a release used in the deployment");
|
||||
editor.assertHoverContains("version", "The version of the release to use");
|
||||
editor.assertHoverContains("url", "URL of the release to use");
|
||||
editor.assertHoverContains("sha1", "The SHA1 of the release tarball");
|
||||
|
||||
editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: some-release\n" +
|
||||
" version: <*>\n"
|
||||
);
|
||||
editor.assertCompletionLabels("latest");
|
||||
}
|
||||
|
||||
@Test public void instanceGroupsCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"instance_groups:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
"instance_groups:\n" +
|
||||
"- name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertCompletions(PLAIN_COMPLETION,
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" azs:\n" +
|
||||
" - <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" env:\n" +
|
||||
" <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" instances: <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" jobs:\n" +
|
||||
" - <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" lifecycle: <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" migrated_from:\n" +
|
||||
" - <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" networks:\n" +
|
||||
" - name: <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" persistent_disk_type: <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" stemcell: <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" update:\n" +
|
||||
" <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" vm_extensions:\n" +
|
||||
" - <*>"
|
||||
, // =============
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" vm_type: <*>"
|
||||
|
||||
// , // =============
|
||||
// Not suggested because its deprecated:
|
||||
// "instance_groups:\n" +
|
||||
// "- name: foo-group\n" +
|
||||
// " properties:\n" +
|
||||
// " <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void instanceGroupsHovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"instance_groups:\n" +
|
||||
"- name: redis-master\n" +
|
||||
" properties: {}\n" +
|
||||
" instances: 1\n" +
|
||||
" azs: [z1, z2]\n" +
|
||||
" jobs:\n" +
|
||||
" - name: redis-server\n" +
|
||||
" release: redis\n" +
|
||||
" properties:\n" +
|
||||
" port: 3606\n" +
|
||||
" vm_type: medium\n" +
|
||||
" vm_extensions: [public-lbs]\n" +
|
||||
" stemcell: default\n" +
|
||||
" persistent_disk_type: medium\n" +
|
||||
" networks:\n" +
|
||||
" - name: default\n" +
|
||||
"\n" +
|
||||
"- name: redis-slave\n" +
|
||||
" instances: 2\n" +
|
||||
" azs: [z1, z2]\n" +
|
||||
" jobs:\n" +
|
||||
" - name: redis-server\n" +
|
||||
" release: redis\n" +
|
||||
" properties: {}\n" +
|
||||
" update:\n" +
|
||||
" canaries: 2\n" +
|
||||
" lifecycle: errand\n" +
|
||||
" migrated_from: []\n" +
|
||||
" env: {}\n" +
|
||||
" vm_type: medium\n" +
|
||||
" stemcell: default\n" +
|
||||
" persistent_disk_type: medium\n" +
|
||||
" networks:\n" +
|
||||
" - name: default\n"
|
||||
);
|
||||
editor.assertHoverContains("name", "A unique name used to identify and reference the instance group.");
|
||||
editor.assertHoverContains("azs", "List of AZs associated with this instance group");
|
||||
editor.assertHoverContains("instances", "The number of instances in this group");
|
||||
editor.assertHoverContains("jobs", "Specifies the name and release of jobs that will be installed on each instance.");
|
||||
editor.assertHoverContains("vm_type", "A valid VM type name from the cloud config");
|
||||
editor.assertHoverContains("vm_extensions", "A valid list of VM extension names from the cloud config");
|
||||
editor.assertHoverContains("stemcell", "A valid stemcell alias from the Stemcells Block");
|
||||
editor.assertHoverContains("persistent_disk_type", "A valid disk type name from the cloud config.");
|
||||
editor.assertHoverContains("networks", "Specifies the networks this instance requires");
|
||||
editor.assertHoverContains("update", "Specific update settings for this instance group");
|
||||
editor.assertHoverContains("migrated_from", "Specific migration settings for this instance group.");
|
||||
editor.assertHoverContains("lifecycle", "Specifies the kind of workload");
|
||||
editor.assertHoverContains("properties", "Specifies instance group properties");
|
||||
editor.assertHoverContains("env", "Specifies advanced BOSH Agent configuration");
|
||||
}
|
||||
|
||||
@Test public void instanceGroups_job_hovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: foo\n" +
|
||||
" jobs:\n" +
|
||||
" - name: the-job\n" +
|
||||
" release: the-jobs-release\n" +
|
||||
" properties:\n" +
|
||||
" blah: blah\n" +
|
||||
" consumes:\n" +
|
||||
" blah: blah \n" +
|
||||
" provides:\n" +
|
||||
" blah: blah\n"
|
||||
);
|
||||
editor.assertHoverContains("name", 3, "The job name");
|
||||
editor.assertHoverContains("release", "The release where the job exists");
|
||||
editor.assertHoverContains("consumes", "Links consumed by the job");
|
||||
editor.assertHoverContains("provides", "Links provided by the job");
|
||||
editor.assertHoverContains("properties", "Specifies job properties");
|
||||
}
|
||||
|
||||
@Test public void instanceGroups_network_hovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: foo\n" +
|
||||
" networks:\n" +
|
||||
" - name: the-network\n" +
|
||||
" static_ips: []\n" +
|
||||
" default: []\n"
|
||||
);
|
||||
editor.assertHoverContains("name", 3, "A valid network name from the cloud config");
|
||||
editor.assertHoverContains("static_ips", "Array of IP addresses");
|
||||
editor.assertHoverContains("default", "Specifies which network components");
|
||||
}
|
||||
|
||||
@Test public void instanceGroups_env_hovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: foo\n" +
|
||||
" env:\n" +
|
||||
" bosh: {}\n" +
|
||||
" password: []\n"
|
||||
);
|
||||
editor.assertHoverContains("bosh", "no description");
|
||||
editor.assertHoverContains("password", "Crypted password");
|
||||
}
|
||||
|
||||
@Test public void updateBlockCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"update:\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertCompletions(PLAIN_COMPLETION,
|
||||
"update:\n" +
|
||||
" canaries: <*>"
|
||||
, // =====
|
||||
"update:\n" +
|
||||
" canary_watch_time: <*>"
|
||||
, // =====
|
||||
"update:\n" +
|
||||
" max_in_flight: <*>"
|
||||
, // =====
|
||||
"update:\n" +
|
||||
" serial: <*>"
|
||||
, // =====
|
||||
"update:\n" +
|
||||
" update_watch_time: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void updateBlockHovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"update:\n" +
|
||||
" canaries: 1\n" +
|
||||
" max_in_flight: 10\n" +
|
||||
" canary_watch_time: 1000-30000\n" +
|
||||
" update_watch_time: 1000-30000\n" +
|
||||
" serial: false"
|
||||
);
|
||||
editor.assertHoverContains("canaries", "The number of [canary]");
|
||||
editor.assertHoverContains("max_in_flight", "maximum number of non-canary instances");
|
||||
editor.assertHoverContains("canary_watch_time", "checks whether the canary instances");
|
||||
editor.assertHoverContains("update_watch_time", "checks whether the instances");
|
||||
editor.assertHoverContains("serial", "deployed in parallel");
|
||||
}
|
||||
|
||||
@Test public void variablesBlockCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"variables:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
"variables:\n" +
|
||||
"- name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"variables:\n" +
|
||||
"- name: foo\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertCompletions(PLAIN_COMPLETION,
|
||||
"variables:\n" +
|
||||
"- name: foo\n" +
|
||||
" options:\n" +
|
||||
" <*>"
|
||||
, // ===============
|
||||
"variables:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"variables:\n" +
|
||||
"- name: foo\n" +
|
||||
" type: <*>"
|
||||
);
|
||||
editor.assertCompletionLabels("certificate", "password", "rsa", "ssh");
|
||||
}
|
||||
|
||||
@Test public void variablesBlockHovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"variables:\n" +
|
||||
"- name: admin_password\n" +
|
||||
" type: password\n" +
|
||||
"- name: default_ca\n" +
|
||||
" type: certificate\n" +
|
||||
" options:\n" +
|
||||
" is_ca: true\n" +
|
||||
" common_name: some-ca\n" +
|
||||
"- name: director_ssl\n" +
|
||||
" type: certificate\n" +
|
||||
" options:\n" +
|
||||
" ca: default_ca\n" +
|
||||
" common_name: cc.cf.internal\n" +
|
||||
" alternative_names: [cc.cf.internal]"
|
||||
);
|
||||
editor.assertHoverContains("name", "Unique name used to identify a variable");
|
||||
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*/);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"director_uuid: dca5480a-6b0e-11e7-907b-a6006ad3dba0\n" +
|
||||
"networks: {}" //This makes it a V1 schema
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(
|
||||
"director_uuid|bosh v2 CLI no longer checks or requires",
|
||||
"networks|Deprecated: 'networks' is a V1 schema property"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void documentSymbols() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"variables:\n" +
|
||||
"- name: blobstore_admin_users_password\n" +
|
||||
" type: password\n" +
|
||||
"- name: blobstore_secure_link_secret\n" +
|
||||
" type: password\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: default\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: '3421.11'\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" networks:\n" +
|
||||
" - name: the-network\n" +
|
||||
" static_ips: []\n" +
|
||||
" default: []\n" +
|
||||
"- name: bar-group\n" +
|
||||
" networks:\n" +
|
||||
" - name: the-network\n" +
|
||||
" static_ips: []\n" +
|
||||
" default: []\n" +
|
||||
"releases:\n" +
|
||||
"- name: one-release\n" +
|
||||
"- name: other-release\n"
|
||||
);
|
||||
editor.assertDocumentSymbols(
|
||||
"default|StemcellAlias",
|
||||
"foo-group|InstanceGroup",
|
||||
"bar-group|InstanceGroup",
|
||||
"one-release|Release",
|
||||
"other-release|Release",
|
||||
"blobstore_admin_users_password|Variable",
|
||||
"blobstore_secure_link_secret|Variable"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void duplicateSymbolChecking() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"variables:\n" +
|
||||
"- name: dup_var\n" +
|
||||
" type: password\n" +
|
||||
"- name: blobstore_admin_users_password\n" +
|
||||
" type: password\n" +
|
||||
"- name: blobstore_secure_link_secret\n" +
|
||||
" type: password\n" +
|
||||
"- name: dup_var\n" +
|
||||
" type: ssh\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: default\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: '3421.11'\n" +
|
||||
"- alias: dup_cell\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: '3421.11'\n" +
|
||||
"- alias: dup_cell\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: '3421.11'\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: foo-group\n" +
|
||||
" networks:\n" +
|
||||
" - name: the-network\n" +
|
||||
" static_ips: []\n" +
|
||||
" default: []\n" +
|
||||
"- name: bar-group\n" +
|
||||
" networks:\n" +
|
||||
" - name: the-network\n" +
|
||||
" static_ips: []\n" +
|
||||
" default: []\n" +
|
||||
"- name: bar-group\n" +
|
||||
" networks:\n" +
|
||||
" - name: the-network\n" +
|
||||
" static_ips: []\n" +
|
||||
" default: []\n" +
|
||||
"releases:\n" +
|
||||
"- name: one-release\n" +
|
||||
"- name: other-release\n" +
|
||||
"- name: one-release\n"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
|
||||
editor.assertProblems(
|
||||
"dup_var|Duplicate 'VariableName'",
|
||||
"dup_var|Duplicate 'VariableName'",
|
||||
"dup_cell|Duplicate 'StemcellAlias'",
|
||||
"dup_cell|Duplicate 'StemcellAlias'",
|
||||
"bar-group|Duplicate 'InstanceGroupName'",
|
||||
"bar-group|Duplicate 'InstanceGroupName'",
|
||||
"one-release|Duplicate 'ReleaseName'",
|
||||
"one-release|Duplicate 'ReleaseName'"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void contentAssistReleaseReference() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" jobs:\n" +
|
||||
" - release: <*>\n" +
|
||||
"releases: \n" +
|
||||
"- name: some-release\n" +
|
||||
" url: https://release-hub.info/some-release.tar.gz?version=99.3.2\n" +
|
||||
" sha1: asddsfsd\n" +
|
||||
"- name: other-release\n" +
|
||||
" url: https://release-hub.info/other-release.tar.gz?version=99.3.2\n" +
|
||||
" sha1: asddsfsd\n"
|
||||
);
|
||||
editor.assertContextualCompletions("<*>"
|
||||
, // ==>
|
||||
"other-release<*>", "some-release<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void reconcileReleaseReference() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" jobs:\n" +
|
||||
" - release: some-release\n" +
|
||||
"- name: some-other-server\n" +
|
||||
" jobs:\n" +
|
||||
" - release: bogus-release\n" +
|
||||
"releases: \n" +
|
||||
"- name: some-release\n" +
|
||||
" url: https://release-hub.info/some-release.tar.gz?version=99.3.2\n" +
|
||||
" sha1: asddsfsd\n" +
|
||||
"- name: other-release\n" +
|
||||
" url: https://release-hub.info/other-release.tar.gz?version=99.3.2\n" +
|
||||
" sha1: asddsfsd\n"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(
|
||||
"bogus-release|unknown 'ReleaseName'. Valid values are: [other-release, some-release]"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void contentAssistStemcellReference() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" stemcell: <*>\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: default\n" +
|
||||
" os: ubuntu\n" +
|
||||
" version: 1346.77.1\n" +
|
||||
"- alias: windoze\n" +
|
||||
" os: windows\n" +
|
||||
" version: 678.9.1\n"
|
||||
);
|
||||
editor.assertContextualCompletions("<*>"
|
||||
, // ==>
|
||||
"default<*>", "windoze<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void reconcileStemcellReference() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" stemcell: default\n" +
|
||||
"- name: windoze-server\n" +
|
||||
" stemcell: windoze\n" +
|
||||
"- name: bad-server\n" +
|
||||
" stemcell: bogus-stemcell\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: default\n" +
|
||||
" os: ubuntu\n" +
|
||||
" version: 1346.77.1\n" +
|
||||
"- alias: windoze\n" +
|
||||
" os: windows\n" +
|
||||
" version: 678.9.1\n"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(
|
||||
"bogus-stemcell|unknown 'StemcellAlias'. Valid values are: [default, windoze]"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void contentAssistVMtype() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" stemcell: windoze\n" +
|
||||
" vm_type: <*>"
|
||||
);
|
||||
editor.assertContextualCompletions(
|
||||
"<*>"
|
||||
, // ==>
|
||||
"default<*>",
|
||||
"large<*>"
|
||||
);
|
||||
|
||||
//Verify that the cache is working. Shouldn't read the cc provier more than once, evem for multiple CA requests.
|
||||
editor.assertCompletionLabels("default", "large");
|
||||
editor.assertCompletionLabels("default", "large");
|
||||
assertEquals(1, cloudConfigProvider.getReadCount());
|
||||
}
|
||||
|
||||
@Test public void reconcileVMtype() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" vm_type: bogus-vm\n" +
|
||||
"- name: other-server\n" +
|
||||
" vm_type: large"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(
|
||||
"bogus-vm|unknown 'VMType'. Valid values are: [default, large]"
|
||||
);
|
||||
assertEquals(1, cloudConfigProvider.getReadCount());
|
||||
}
|
||||
|
||||
@Test public void reconcileVMTypeWhenCloudConfigUnavailable() throws Exception {
|
||||
cloudConfigProvider.readWith(() -> null);
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" vm_type: bogus-vm\n" +
|
||||
"- name: other-server\n" +
|
||||
" vm_type: large"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(/*NONE*/); //Should not complain about unknown vm_types, if we can't determine what valid vm_types actually exist.
|
||||
}
|
||||
|
||||
@Test public void reconcileVMTypeWhenCloudConfigThrows() throws Exception {
|
||||
cloudConfigProvider.readWith(() -> { throw new TimeoutException("Reading cloud config timed out"); });
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" vm_type: bogus-vm\n" +
|
||||
"- name: other-server\n" +
|
||||
" vm_type: large"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(/*NONE*/); //Should not complain about unknown vm_types, if we can't determine what valid vm_types actually exist.
|
||||
}
|
||||
|
||||
@Test public void contentAssistShowsWarningWhenCloudConfigThrows() throws Exception {
|
||||
cloudConfigProvider.readWith(() -> {
|
||||
throw new TimeoutException("Reading cloud config timed out");
|
||||
});
|
||||
Editor editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"instance_groups: \n" +
|
||||
"- name: some-server\n" +
|
||||
" vm_type: <*>"
|
||||
);
|
||||
CompletionItem completion = editor.assertCompletionLabels("TimeoutException").get(0);
|
||||
completion = harness.resolveCompletionItem(completion);
|
||||
assertContains("Reading cloud config timed out", completion.getDocumentation());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,8 +8,7 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -20,7 +19,7 @@ import java.nio.file.Paths;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncKind;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.bosh.BoshLanguageServer;
|
||||
import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
|
||||
public class BoshLanguageServerTest {
|
||||
@@ -31,7 +30,9 @@ public class BoshLanguageServerTest {
|
||||
|
||||
@Test
|
||||
public void createAndInitializeServerWithWorkspace() throws Exception {
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() -> new BoshLanguageServer());
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() ->
|
||||
new BoshLanguageServer(new MockCloudConfigProvider())
|
||||
);
|
||||
File workspaceRoot = getTestResource("/workspace/");
|
||||
assertExpectedInitResult(harness.intialize(workspaceRoot));
|
||||
}
|
||||
@@ -39,7 +40,9 @@ public class BoshLanguageServerTest {
|
||||
@Test
|
||||
public void createAndInitializeServerWithoutWorkspace() throws Exception {
|
||||
File workspaceRoot = null;
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() -> new BoshLanguageServer());
|
||||
LanguageServerHarness harness = new LanguageServerHarness(() ->
|
||||
new BoshLanguageServer(new MockCloudConfigProvider())
|
||||
);
|
||||
assertExpectedInitResult(harness.intialize(workspaceRoot));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.bosh.BoshValueParsers;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
|
||||
public class BoshValueParserTest {
|
||||
|
||||
private static final String MARKER = "<*>";
|
||||
|
||||
@Test public void integerOrRangeOkay() throws Exception {
|
||||
BoshValueParsers.INTEGER_OR_RANGE.parse("123");
|
||||
BoshValueParsers.INTEGER_OR_RANGE.parse("123-456");
|
||||
}
|
||||
|
||||
@Test public void integerOrRangeGarbage() throws Exception {
|
||||
assertProblem(BoshValueParsers.INTEGER_OR_RANGE, "<*>garbage<*>", "Should be either a Integer, or a range (of the form '<integer>-<integer>')");
|
||||
assertProblem(BoshValueParsers.INTEGER_OR_RANGE, "<*>123--456<*>", "Should be either a Integer, or a range (of the form '<integer>-<integer>')");
|
||||
assertProblem(BoshValueParsers.INTEGER_OR_RANGE, "<*>garbage<*>-123", "Should be a Integer");
|
||||
assertProblem(BoshValueParsers.INTEGER_OR_RANGE, "123-<*>garbage<*>", "Should be a Integer");
|
||||
assertProblem(BoshValueParsers.INTEGER_OR_RANGE, "<*>123-122<*>", "123 should be smaller than 122");
|
||||
}
|
||||
|
||||
@Test public void urlOkay() throws Exception {
|
||||
ValueParser urlParser = BoshValueParsers.url("http", "https", "file");
|
||||
urlParser.parse("http://foobar.com/munhings.tar.gz");
|
||||
urlParser.parse("https://foobar.com/munhings.tar.gz");
|
||||
urlParser.parse("hTTp://foobar.com/munhings.tar.gz");
|
||||
urlParser.parse("HTTPS://foobar.com/munhings.tar.gz");
|
||||
urlParser.parse("file://local/file");
|
||||
urlParser.parse("file:///local/file");
|
||||
urlParser.parse("FILE:///local/file");
|
||||
}
|
||||
|
||||
@Test public void urlGarbage() throws Exception {
|
||||
ValueParser urlParser = BoshValueParsers.url("http", "https", "file");
|
||||
assertProblem(urlParser, "<*>woot<*>://foobar.com", "Url scheme must be one of [http, https, file]");
|
||||
assertProblem(urlParser, "<*>wOOt<*>://foobar.com", "Url scheme must be one of [http, https, file]");
|
||||
}
|
||||
|
||||
private void assertProblem(ValueParser parser, String input, String expectedMessage) throws Exception {
|
||||
String unmarkedInput = input.replace(MARKER, "");
|
||||
int firstMarker = input.indexOf(MARKER);
|
||||
assertTrue(firstMarker>=0);
|
||||
int secondMarker = input.indexOf(MARKER, firstMarker+1)-MARKER.length();
|
||||
assertTrue(secondMarker>=firstMarker);
|
||||
try {
|
||||
parser.parse(unmarkedInput);
|
||||
} catch (ValueParseException e) {
|
||||
int startIndex = startIndex(unmarkedInput, e);
|
||||
int endIndex = endIndex(unmarkedInput, e);
|
||||
String markedInput =
|
||||
unmarkedInput.substring(0, startIndex) +
|
||||
MARKER +
|
||||
unmarkedInput.substring(startIndex, endIndex) +
|
||||
MARKER +
|
||||
unmarkedInput.substring(endIndex);
|
||||
assertEquals(input, markedInput);
|
||||
assertEquals(expectedMessage, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private int endIndex(String unmarkedInput, ValueParseException e) {
|
||||
int i = e.getEndIndex();
|
||||
if (i>=0) {
|
||||
return i;
|
||||
} else {
|
||||
return unmarkedInput.length();
|
||||
}
|
||||
}
|
||||
|
||||
private int startIndex(String unarkedInput, ValueParseException e) {
|
||||
int i = e.getStartIndex();
|
||||
if (i>=0) {
|
||||
return i;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*******************************************************************************
|
||||
* 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.mocks;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.ide.vscode.bosh.BoshCommandCloudConfigProviderTest;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.commons.util.IOUtil;
|
||||
|
||||
public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvider {
|
||||
|
||||
static final String MOCK_DATA_RSRC = "/cmd-out/cloud-config.json";
|
||||
|
||||
private Callable<String> cloudConfigReader = () -> IOUtil.toString(BoshCommandCloudConfigProviderTest.class.getResourceAsStream(MOCK_DATA_RSRC));
|
||||
|
||||
private int readCount = 0;
|
||||
|
||||
/**
|
||||
* Override with a 'fake' which just returns some mock data. That way we can unit-test
|
||||
* without requiring a real bosh setup.
|
||||
*/
|
||||
@Override
|
||||
protected String executeBoshCloudConfigCommand() throws Exception {
|
||||
readCount++;
|
||||
return cloudConfigReader.call();
|
||||
}
|
||||
|
||||
public MockCloudConfigProvider readWith(Callable<String> reader) {
|
||||
this.cloudConfigReader = reader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getReadCount() {
|
||||
return readCount;
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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.concourse;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.bosh.BoshLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
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;
|
||||
|
||||
@Before public void setup() throws Exception {
|
||||
harness = new LanguageServerHarness(() -> {
|
||||
return new BoshLanguageServer()
|
||||
.setMaxCompletions(100);
|
||||
},
|
||||
LanguageId.BOSH_DEPLOYMENT
|
||||
);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test public void toplevelV2PropertyNamesKnown() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"name: some-name\n" +
|
||||
"director_uuid: cf8dc1fc-9c42-4ffc-96f1-fbad983a6ce6\n" +
|
||||
"releases:\n" +
|
||||
"- name: redis\n" +
|
||||
" version: 12\n" +
|
||||
"stemcells:\n" +
|
||||
"- alias: default\n" +
|
||||
" os: ubuntu-trusty\n" +
|
||||
" version: 3421.11\n" +
|
||||
"update:\n" +
|
||||
" canaries: 1\n" +
|
||||
" max_in_flight: 10\n" +
|
||||
" canary_watch_time: 1000-30000\n" +
|
||||
" update_watch_time: 1000-30000\n" +
|
||||
"instance_groups:\n" +
|
||||
"- name: redis-master\n" +
|
||||
" instances: 1\n" +
|
||||
" azs: [z1, z2]\n" +
|
||||
" jobs:\n" +
|
||||
" - name: redis-server\n" +
|
||||
" release: redis\n" +
|
||||
" properties:\n" +
|
||||
" port: 3606\n" +
|
||||
" vm_type: medium\n" +
|
||||
" vm_extensions: [public-lbs]\n" +
|
||||
" stemcell: default\n" +
|
||||
" persistent_disk_type: medium\n" +
|
||||
" networks:\n" +
|
||||
" - name: default\n" +
|
||||
"- name: redis-slave\n" +
|
||||
" instances: 2\n" +
|
||||
" azs: [z1, z2]\n" +
|
||||
" jobs:\n" +
|
||||
" - name: redis-server\n" +
|
||||
" release: redis\n" +
|
||||
" properties: {}\n" +
|
||||
" vm_type: medium\n" +
|
||||
" stemcell: default\n" +
|
||||
" persistent_disk_type: medium\n" +
|
||||
" networks:\n" +
|
||||
" - name: default\n" +
|
||||
"variables:\n" +
|
||||
"- name: admin_password\n" +
|
||||
" type: password\n" +
|
||||
"- name: default_ca\n" +
|
||||
" type: certificate\n" +
|
||||
" options:\n" +
|
||||
" is_ca: true\n" +
|
||||
" common_name: some-ca\n" +
|
||||
"- name: director_ssl\n" +
|
||||
" type: certificate\n" +
|
||||
" options:\n" +
|
||||
" ca: default_ca\n" +
|
||||
" common_name: cc.cf.internal\n" +
|
||||
" alternative_names: [cc.cf.internal]\n" +
|
||||
"properties:\n" +
|
||||
" a-property: the-value\n" +
|
||||
"tags:\n" +
|
||||
" project: cf\n" +
|
||||
"blah: hoooo\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"properties|Deprecated in favor of job level properties and links",
|
||||
"blah|Unknown property"
|
||||
);
|
||||
|
||||
editor.assertHoverContains("name", "The name of the deployment");
|
||||
editor.assertHoverContains("director_uuid", "This string must match the UUID of the currently targeted Director");
|
||||
editor.assertHoverContains("releases", "The name and version of each release in the deployment");
|
||||
editor.assertHoverContains("stemcells", "The name and version of each stemcell");
|
||||
editor.assertHoverContains("update", "This specifies instance update properties");
|
||||
editor.assertHoverContains("instance_groups", "Specifies the mapping between release [jobs](https://bosh.io/docs/terminology.html#job) and instance groups.");
|
||||
editor.assertHoverContains("properties", 3, "Describes global properties. Deprecated");
|
||||
editor.assertHoverContains("variables", "Describes variables");
|
||||
editor.assertHoverContains("tags", "Specifies key value pairs to be sent to the CPI for VM tagging");
|
||||
}
|
||||
|
||||
@Test public void toplevelV2PropertyCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"<*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
"name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"name: blah\n" +
|
||||
"<*>"
|
||||
);
|
||||
|
||||
editor.assertCompletions(
|
||||
"name: blah\n" +
|
||||
"director_uuid: <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"instance_groups:\n- <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"releases:\n- <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"stemcells:\n- <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"tags:\n <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"update:\n <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"variables:\n- <*>"
|
||||
, // ============
|
||||
"name: blah\n" +
|
||||
"properties:\n <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void stemcellCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"stemcells:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
"stemcells:\n" +
|
||||
"- alias: <*>"
|
||||
, // ==========
|
||||
"stemcells:\n" +
|
||||
"- name: <*>"
|
||||
, // ==========
|
||||
"stemcells:\n" +
|
||||
"- os: <*>"
|
||||
, // ==========
|
||||
"stemcells:\n" +
|
||||
"- version: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"stemcells:\n" +
|
||||
"- alias<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void stemcellReconciling() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"stemcells:\n" +
|
||||
"- {}"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"-|One of [name, os] is required",
|
||||
"-|[alias, version] are required",
|
||||
"}|[director_uuid, instance_groups, name, releases, update] are required"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void releasesBlockCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertCompletions(
|
||||
"releases:\n" +
|
||||
"- name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: foo\n" +
|
||||
" <*>"
|
||||
);
|
||||
editor.assertCompletions(PLAIN_COMPLETION,
|
||||
"releases:\n" +
|
||||
"- name: foo\n" +
|
||||
" version: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void releasesBlockReconcileAndHovers() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: some-release\n" +
|
||||
" version: some-version\n" +
|
||||
" woot: dunno\n"
|
||||
);
|
||||
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
|
||||
editor.assertProblems(
|
||||
"woot|Unknown property"
|
||||
);
|
||||
|
||||
editor.assertHoverContains("name", "Name of a release used in the deployment");
|
||||
editor.assertHoverContains("version", "The version of the release to use");
|
||||
|
||||
editor = harness.newEditor(
|
||||
"releases:\n" +
|
||||
"- name: some-release\n" +
|
||||
" version: <*>\n"
|
||||
);
|
||||
editor.assertCompletionLabels("latest");
|
||||
}
|
||||
|
||||
@Test public void instanceGroupsCompletions() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"instance_groups:\n" +
|
||||
"- <*>"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Tables": null,
|
||||
"Blocks": [
|
||||
"azs:\n- cloud_properties:\n datacenters:\n - clusters:\n - AppFabric: {}\n name: z1\n- cloud_properties:\n datacenters:\n - clusters:\n - AppFabric: {}\n name: z2\n- cloud_properties:\n datacenters:\n - clusters:\n - AppFabric: {}\n name: z3\ncompilation:\n az: z1\n network: default\n reuse_compilation_vms: true\n vm_type: default\n workers: 5\ndisk_types:\n- disk_size: 3000\n name: default\n- disk_size: 50000\n name: large\nnetworks:\n- name: default\n subnets:\n - azs:\n - z1\n - z2\n - z3\n cloud_properties:\n name: VLAN 40 - AF\n dns:\n - 10.192.2.10\n - 8.8.8.8\n gateway: 10.194.4.1\n range: 10.194.4.0/23\n reserved:\n - 10.194.4.1-10.194.4.34\n - 10.194.4.40-10.194.5.255\n type: manual\nvm_types:\n- cloud_properties:\n cpu: 2\n disk: 3240\n ram: 1024\n name: default\n- cloud_properties:\n cpu: 2\n disk: 30240\n ram: 4096\n name: large\n"
|
||||
],
|
||||
"Lines": [
|
||||
"Using environment '10.194.4.35' as client 'admin'",
|
||||
"Succeeded"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
||||
resources:
|
||||
- name: sts4
|
||||
type: git
|
||||
source:
|
||||
repository: https://github.com/kdvolder/somestuff
|
||||
- name: other-repo
|
||||
type: git
|
||||
source:
|
||||
repository: https://github.com/kdvolder/somestuff
|
||||
- name: more-stuff
|
||||
type: git
|
||||
source:
|
||||
repository: https://github.com/kdvolder/somestuff
|
||||
jobs:
|
||||
- name: job1
|
||||
plan:
|
||||
- get: sts4
|
||||
- task: do-stuff
|
||||
input_mapping:
|
||||
task_input: bogus_input
|
||||
repo: sts4
|
||||
- get: bogus-get
|
||||
- put: bogus-put
|
||||
@@ -1,115 +0,0 @@
|
||||
##########################################################
|
||||
resource_types:
|
||||
- name: s3-multi
|
||||
type: docker-image
|
||||
source:
|
||||
repository: kdvolder/s3-resource-simple
|
||||
- name: slack-notification
|
||||
type: docker-image
|
||||
source:
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
#########################################################
|
||||
resources:
|
||||
- name: docker-git
|
||||
type: git
|
||||
source:
|
||||
uri: git@github.com:spring-projects/sts4.git
|
||||
branch: {{branch}}
|
||||
username: kdvolder
|
||||
private_key: {{rsa_id}}
|
||||
paths:
|
||||
- concourse/docker
|
||||
- name: sts4
|
||||
type: git
|
||||
source:
|
||||
uri: git@github.com:spring-projects/sts4.git
|
||||
branch: {{branch}}
|
||||
private_key: {{rsa_id}}
|
||||
- name: s3-boot-properties-vsix
|
||||
type: s3
|
||||
source:
|
||||
bucket: {{s3_bucket}}
|
||||
access_key_id: {{s3_accesskey}}
|
||||
secret_access_key: {{s3_secretkey}}
|
||||
region_name: {{s3_region}}
|
||||
regexp: sts4/vscode-extensions/vscode-boot-properties-(.*).vsix
|
||||
- name: s3-manifest-yaml-vsix
|
||||
type: s3
|
||||
source:
|
||||
bucket: {{s3_bucket}}
|
||||
access_key_id: {{s3_accesskey}}
|
||||
secret_access_key: {{s3_secretkey}}
|
||||
region_name: {{s3_region}}
|
||||
regexp: sts4/vscode-extensions/vscode-manifest-yaml-(.*).vsix
|
||||
- name: website
|
||||
type: s3-multi
|
||||
source:
|
||||
bucket: {{s3_prod_bucket}}
|
||||
access_key_id: {{s3_prod_accesskey}}
|
||||
secret_access_key: {{s3_prod_secretkey}}
|
||||
region_name: {{s3_region}}
|
||||
path: snapshot/STS4/vscode-extensions
|
||||
options:
|
||||
- "--acl public-read"
|
||||
- name: slack-notification
|
||||
type: slack-notification
|
||||
source:
|
||||
url: https://hooks.slack.com/services/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
|
||||
- name: docker-image
|
||||
type: docker-image
|
||||
source:
|
||||
username: {{docker_hub_username}}
|
||||
password: {{docker_hub_password}}
|
||||
repository: kdvolder/sts4-build-env
|
||||
########################################################################################
|
||||
jobs:
|
||||
- name: build-docker-image
|
||||
serial: true
|
||||
plan:
|
||||
- get: docker-git
|
||||
trigger: true
|
||||
- put: docker-image
|
||||
params:
|
||||
build: docker-git/concourse/docker
|
||||
get_params:
|
||||
skip_download: true
|
||||
- name: build-vsix
|
||||
plan:
|
||||
- get: sts4
|
||||
trigger: true
|
||||
- task: build-vscode-extensions
|
||||
file: sts4/concourse/tasks/build-vscode-extensions.yml
|
||||
on_success:
|
||||
aggregate:
|
||||
- put: s3-manifest-yaml-vsix
|
||||
params:
|
||||
file: vsix-files/vscode-manifest-yaml-*.vsix
|
||||
acl: public-read
|
||||
- put: s3-boot-properties-vsix
|
||||
params:
|
||||
file: vsix-files/vscode-boot-properties-*.vsix
|
||||
acl: public-read
|
||||
on_failure:
|
||||
put: slack-notification
|
||||
params:
|
||||
channel: "@kdvolder"
|
||||
text: |
|
||||
Concourse ${BUILD_PIPELINE_NAME}/${BUILD_JOB_NAME}/${BUILD_NAME} has failed!
|
||||
- name: build-website
|
||||
plan:
|
||||
- aggregate:
|
||||
- get: sts4
|
||||
- get: s3-manifest-yaml-vsix
|
||||
passed:
|
||||
- build-vsix
|
||||
trigger: true
|
||||
- get: s3-boot-properties-vsix
|
||||
passed:
|
||||
- build-vsix
|
||||
trigger: true
|
||||
- task: build-website
|
||||
file: sts4/concourse/tasks/build-website.yml
|
||||
- put: website
|
||||
params:
|
||||
path: website
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
workdir=`pwd`
|
||||
|
||||
cd sts4/vscode-extensions
|
||||
./build-all.sh
|
||||
|
||||
cd $workdir
|
||||
cp `find sts4/vscode-extensions -name "*.vsix"` vsix-files
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
inputs:
|
||||
- name: sts4
|
||||
outputs:
|
||||
- name: vsix-files
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: docker-image
|
||||
source:
|
||||
repository: kdvolder/sts4-build-env
|
||||
run:
|
||||
path: "sts4/concourse/tasks/build-vscode-extensions.sh"
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
workdir=`pwd`
|
||||
sources=$workdir/sts4/eclipse-distribution/common/html
|
||||
target=$workdir/website
|
||||
|
||||
#cp -r "${sources}/stylesheet.css" "$target"
|
||||
#cp -r ${sources}/*.js "$target"
|
||||
#cp s3-manifest-yaml-vsix/*.vsix "$target"
|
||||
#cp s3-boot-properties-vsix/*.vsix "$target"
|
||||
|
||||
export vscode_manifest_yaml=$(basename s3-manifest-yaml-vsix/*.vsix)
|
||||
echo "vscode_manifest_yaml=$vscode_manifest_yaml"
|
||||
export vscode_boot_properties=$(basename s3-boot-properties-vsix/*.vsix)
|
||||
echo "vscode_boot_properties=$vscode_boot_properties"
|
||||
|
||||
envsubst > "$target/vscode-extensions-snippet.html" << XXXXXX
|
||||
<ul>
|
||||
<li>Spring Boot Property Language Server:
|
||||
<a href="http://s3-test.spring.io/sts4/vscode-extensions/${vscode_boot_properties}">${vscode_boot_properties}</a>
|
||||
</li>
|
||||
<li>Cloud Foundry Manifest Language Server:
|
||||
<a href="http://s3-test.spring.io/sts4/vscode-extensions/${vscode_manifest_yaml}">${vscode_manifest_yaml}</a>
|
||||
</li>
|
||||
</ul>
|
||||
XXXXXX
|
||||
|
||||
export vscode_snippet=`cat "$target/vscode-extensions-snippet.html"`
|
||||
|
||||
envsubst > "$target/vscode-extensions.html" << XXXXXX
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h1>STS4 Vscode Extensions</h1>
|
||||
|
||||
$vscode_snippet
|
||||
|
||||
</body>
|
||||
</html>
|
||||
XXXXXX
|
||||
|
||||
cat $target/vscode-extensions.html
|
||||
@@ -1,13 +0,0 @@
|
||||
inputs:
|
||||
- name: sts4
|
||||
- name: s3-manifest-yaml-vsix
|
||||
- name: s3-boot-properties-vsix
|
||||
outputs:
|
||||
- name: website
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: docker-image
|
||||
source:
|
||||
repository: kdvolder/sts4-build-env
|
||||
run:
|
||||
path: "sts4/concourse/tasks/build-website.sh"
|
||||
@@ -211,7 +211,7 @@ public class SpringBootApp {
|
||||
if (env != null) {
|
||||
JSONObject portsObject = env.getJSONObject("server.ports");
|
||||
if (portsObject != null) {
|
||||
String portValue = portsObject.getString("local.server.port");
|
||||
String portValue = portsObject.get("local.server.port").toString();
|
||||
return portValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<version>${mockito-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.ExternalCommand;
|
||||
import org.springframework.ide.vscode.commons.util.ExternalProcess;
|
||||
@@ -28,7 +29,7 @@ public class MavenBuilder {
|
||||
|
||||
private List<String> properties;
|
||||
|
||||
public void execute() throws IOException, InterruptedException {
|
||||
public void execute() throws IOException, InterruptedException, TimeoutException {
|
||||
Path mvnwPath = System.getProperty("os.name").toLowerCase().startsWith("win") ? projectPath.resolve("mvnw.cmd")
|
||||
: projectPath.resolve("mvnw");
|
||||
mvnwPath.toFile().setExecutable(true);
|
||||
|
||||
@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.commons.util;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Encapsulates information about an 'external' command that can be run through the OS.
|
||||
@@ -68,7 +69,7 @@ public class ExternalCommand {
|
||||
* result of commands are logged to the console and if the command returns non
|
||||
* 0 exit value an exception is thrown.
|
||||
*/
|
||||
public void exec(File workdir) throws IOException, InterruptedException {
|
||||
public void exec(File workdir) throws IOException, InterruptedException, TimeoutException {
|
||||
System.out.println(">>> exec: "+this);
|
||||
ExternalProcess process = new ExternalProcess(workdir, this);
|
||||
System.out.println(process);
|
||||
|
||||
@@ -16,6 +16,9 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -113,13 +116,14 @@ public class ExternalProcess {
|
||||
* Creates an external process and waits for it to terminate. The output and error streams
|
||||
* will be read and forwarded to System.out and System.err
|
||||
*/
|
||||
public ExternalProcess(File workingDir, ExternalCommand cmd) throws IOException, InterruptedException {
|
||||
this(workingDir, cmd, false);
|
||||
public ExternalProcess(File workingDir, ExternalCommand cmd) throws IOException, InterruptedException, TimeoutException {
|
||||
this(workingDir, cmd, false, null);
|
||||
}
|
||||
|
||||
private void init(File workingDir, ExternalCommand cmd,
|
||||
OutputStream outStream, OutputStream errStream) throws IOException,
|
||||
InterruptedException {
|
||||
OutputStream outStream, OutputStream errStream,
|
||||
Duration timeout) throws IOException,
|
||||
InterruptedException, TimeoutException {
|
||||
this.cmd = cmd;
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(cmd.getProgramAndArgs());
|
||||
processBuilder.directory(workingDir);
|
||||
@@ -127,15 +131,33 @@ public class ExternalProcess {
|
||||
process = processBuilder.start();
|
||||
err = new StreamGobler(process.getErrorStream(), errStream);
|
||||
out = new StreamGobler(process.getInputStream(), outStream);
|
||||
exitValue = process.waitFor();
|
||||
}
|
||||
|
||||
public ExternalProcess(File workingDir, ExternalCommand cmd, boolean captureStreams) throws IOException, InterruptedException {
|
||||
if (captureStreams) {
|
||||
init(workingDir, cmd, new ByteArrayOutputStream(), new ByteArrayOutputStream());
|
||||
if (timeout==null) {
|
||||
exitValue = process.waitFor();
|
||||
} else {
|
||||
init(workingDir, cmd, System.out, System.err);
|
||||
if (process.waitFor(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
exitValue = process.exitValue();
|
||||
} else {
|
||||
process.destroy();
|
||||
exitValue = 999; //Set some non-0 value as that is what some callers might use to determine 'failure' occurred.
|
||||
throw new TimeoutException("Command timed out: "+this);
|
||||
}
|
||||
}
|
||||
if (exitValue!=0) {
|
||||
throw new IOException("Command execution failed:\n"+this);
|
||||
}
|
||||
}
|
||||
|
||||
public ExternalProcess(File workingDir, ExternalCommand cmd, boolean captureStreams, Duration timeout) throws IOException, InterruptedException, TimeoutException {
|
||||
if (captureStreams) {
|
||||
init(workingDir, cmd, new ByteArrayOutputStream(), new ByteArrayOutputStream(), timeout);
|
||||
} else {
|
||||
init(workingDir, cmd, System.out, System.err, timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ExternalProcess(File workingDir, ExternalCommand cmd, boolean captureStreams) throws IOException, InterruptedException, TimeoutException {
|
||||
this(workingDir, cmd, captureStreams, null);
|
||||
}
|
||||
|
||||
public String getOut() throws InterruptedException {
|
||||
@@ -156,13 +178,13 @@ public class ExternalProcess {
|
||||
result.append("exitValue = "+exitValue+"\n");
|
||||
String strOut = getOut();
|
||||
if (strOut!=null) {
|
||||
result.append("\n------- System.out -------\n");
|
||||
result.append("------- System.out -------\n");
|
||||
result.append(strOut);
|
||||
}
|
||||
String strErr = getErr();
|
||||
if (strErr!=null) {
|
||||
result.append("\n------- System.err -------\n");
|
||||
result.append(getOut());
|
||||
result.append("------- System.err -------\n");
|
||||
result.append(strErr);
|
||||
}
|
||||
result.append("<<<< ExternalProcess");
|
||||
return result.toString();
|
||||
|
||||
@@ -21,10 +21,12 @@ public class LanguageId {
|
||||
public static final LanguageId PLAINTEXT = of("plaintext");
|
||||
public static final LanguageId CONCOURSE_TASK = of("concourse-task-yaml");
|
||||
public static final LanguageId CONCOURSE_PIPELINE = of("concourse-pipeline-yaml");
|
||||
public static final LanguageId BOSH_DEPLOYMENT = of("bosh-deployment-manifest");
|
||||
public static final LanguageId CF_MANIFEST = of("manifest-yaml");
|
||||
public static final LanguageId JAVA = of("java");
|
||||
public static final LanguageId YAML = of("yaml");
|
||||
|
||||
public static final LanguageId BOSH_DEPLOYMENT = of("bosh-deployment-manifest");
|
||||
public static final LanguageId BOSH_CLOUD_CONFIG = of("bosh-cloud-config");
|
||||
|
||||
private final String id;
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
eclipse.preferences.version=1
|
||||
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
|
||||
sp_cleanup.add_default_serial_version_id=true
|
||||
sp_cleanup.add_generated_serial_version_id=false
|
||||
sp_cleanup.add_missing_annotations=true
|
||||
sp_cleanup.add_missing_deprecated_annotations=true
|
||||
sp_cleanup.add_missing_methods=false
|
||||
sp_cleanup.add_missing_nls_tags=false
|
||||
sp_cleanup.add_missing_override_annotations=true
|
||||
sp_cleanup.add_missing_override_annotations_interface_methods=true
|
||||
sp_cleanup.add_serial_version_id=false
|
||||
sp_cleanup.always_use_blocks=true
|
||||
sp_cleanup.always_use_parentheses_in_expressions=false
|
||||
sp_cleanup.always_use_this_for_non_static_field_access=false
|
||||
sp_cleanup.always_use_this_for_non_static_method_access=false
|
||||
sp_cleanup.convert_functional_interfaces=false
|
||||
sp_cleanup.convert_to_enhanced_for_loop=false
|
||||
sp_cleanup.correct_indentation=false
|
||||
sp_cleanup.format_source_code=false
|
||||
sp_cleanup.format_source_code_changes_only=false
|
||||
sp_cleanup.insert_inferred_type_arguments=false
|
||||
sp_cleanup.make_local_variable_final=true
|
||||
sp_cleanup.make_parameters_final=false
|
||||
sp_cleanup.make_private_fields_final=true
|
||||
sp_cleanup.make_type_abstract_if_missing_method=false
|
||||
sp_cleanup.make_variable_declarations_final=false
|
||||
sp_cleanup.never_use_blocks=false
|
||||
sp_cleanup.never_use_parentheses_in_expressions=true
|
||||
sp_cleanup.on_save_use_additional_actions=true
|
||||
sp_cleanup.organize_imports=false
|
||||
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
|
||||
sp_cleanup.remove_private_constructors=true
|
||||
sp_cleanup.remove_redundant_type_arguments=false
|
||||
sp_cleanup.remove_trailing_whitespaces=true
|
||||
sp_cleanup.remove_trailing_whitespaces_all=true
|
||||
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
|
||||
sp_cleanup.remove_unnecessary_casts=true
|
||||
sp_cleanup.remove_unnecessary_nls_tags=false
|
||||
sp_cleanup.remove_unused_imports=false
|
||||
sp_cleanup.remove_unused_local_variables=false
|
||||
sp_cleanup.remove_unused_private_fields=true
|
||||
sp_cleanup.remove_unused_private_members=false
|
||||
sp_cleanup.remove_unused_private_methods=true
|
||||
sp_cleanup.remove_unused_private_types=true
|
||||
sp_cleanup.sort_members=false
|
||||
sp_cleanup.sort_members_all=false
|
||||
sp_cleanup.use_anonymous_class_creation=false
|
||||
sp_cleanup.use_blocks=false
|
||||
sp_cleanup.use_blocks_only_for_return_and_throw=false
|
||||
sp_cleanup.use_lambda=true
|
||||
sp_cleanup.use_parentheses_in_expressions=false
|
||||
sp_cleanup.use_this_for_non_static_field_access=false
|
||||
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
|
||||
sp_cleanup.use_this_for_non_static_method_access=false
|
||||
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
|
||||
@@ -116,6 +116,18 @@ public class NodeUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static NodeTuple getPropertyTuple(Node node, String propName) {
|
||||
if (node instanceof MappingNode) {
|
||||
for (NodeTuple entry : ((MappingNode)node).getValue()) {
|
||||
String key = NodeUtil.asScalar(entry.getKeyNode());
|
||||
if (propName.equals(key)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Node getProperty(Node node, String propName) {
|
||||
if (node instanceof MappingNode) {
|
||||
for (NodeTuple entry : ((MappingNode)node).getValue()) {
|
||||
|
||||
@@ -54,7 +54,7 @@ public class AppendTextBuilder {
|
||||
//ready to enter sequence element on next line
|
||||
newline(text, indent);
|
||||
text.append("- ");
|
||||
singleRequiredProperty(typeUtil.getDomainType(type), indent+2, text);
|
||||
singleMostImportantProperty(typeUtil.getDomainType(type), indent+2, text);
|
||||
//Yes using 2 here instead of YamlIndentUtil.INDENT_BY is deliberate. It's the same value (now),
|
||||
// but the 2 used here is the width of the "- " which should determine nested indent level for things to
|
||||
// line up properly.
|
||||
@@ -66,14 +66,18 @@ public class AppendTextBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private void singleRequiredProperty(YType type, int indent, StringBuilder text) {
|
||||
private void singleMostImportantProperty(YType type, int indent, StringBuilder text) {
|
||||
if (type!=null) {
|
||||
YTypedProperty requireProp = Streams.getSingle(typeUtil.getProperties(type).stream()
|
||||
.filter(p -> p.isRequired()));
|
||||
if (requireProp!=null) {
|
||||
text.append(requireProp.getName());
|
||||
YTypedProperty singleProp = Streams.getSingle(typeUtil.getProperties(type).stream()
|
||||
.filter(p -> p.isPrimary()));
|
||||
if (singleProp==null) {
|
||||
singleProp = Streams.getSingle(typeUtil.getProperties(type).stream()
|
||||
.filter(p -> p.isRequired()));
|
||||
}
|
||||
if (singleProp!=null) {
|
||||
text.append(singleProp.getName());
|
||||
text.append(':');
|
||||
build(requireProp.getType(), indent+YamlIndentUtil.INDENT_BY, text);
|
||||
build(singleProp.getType(), indent+YamlIndentUtil.INDENT_BY, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -215,7 +216,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
for (YValueHint value : values) {
|
||||
double score = FuzzyMatcher.matchScore(query, value.getValue());
|
||||
if (score!=0 && !value.equals(query)) {
|
||||
if (score!=0 && value!=null && !query.equals(value.getValue())) {
|
||||
int queryStart = offset-query.length();
|
||||
DocumentEdits edits = new DocumentEdits(doc.getDocument());
|
||||
edits.delete(queryStart, offset);
|
||||
@@ -357,7 +358,7 @@ public class YTypeAssistContext extends AbstractYamlAssistContext {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected Collection<ICompletionProposal> getDashedCompletions(YamlDocument doc, SNode current, int offset) {
|
||||
try {
|
||||
YamlAssistContext relaxed = relaxForDashes();
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -18,13 +19,21 @@ import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ITypeCollector;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -127,5 +136,33 @@ public class ASTTypeCache implements ITypeCollector {
|
||||
return typeIndex.get(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this astTypeCache to extract all defined names for a given type of definition.
|
||||
*/
|
||||
public Collection<String> getDefinedNames(DynamicSchemaContext dc, YType defType) {
|
||||
IDocument doc = dc.getDocument();
|
||||
if (doc!=null) {
|
||||
String uri = doc.getUri();
|
||||
if (uri!=null) {
|
||||
NodeTypes typeMap = getNodeTypes(uri);
|
||||
if (typeMap!=null) {
|
||||
Collection<Node> nodes = typeMap.getNodes(defType);
|
||||
if (nodes!=null) {
|
||||
ImmutableSet.Builder<String> builder = ImmutableSortedSet.naturalOrder();
|
||||
for (Node node : nodes) {
|
||||
String name = NodeUtil.asScalar(node);
|
||||
if (StringUtil.hasText(name)) {
|
||||
builder.add(name);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.ASTDynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
@@ -129,11 +130,11 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
return allOf(ast, node);
|
||||
}
|
||||
|
||||
private void reconcile(YamlFileAST ast, YamlPath path, Node parent, Node node, YType type) {
|
||||
private void reconcile(YamlFileAST ast, YamlPath path, Node parent, Node node, YType _type) {
|
||||
// IDocument doc = ast.getDocument();
|
||||
if (type!=null) {
|
||||
if (_type!=null) {
|
||||
DynamicSchemaContext schemaContext = new ASTDynamicSchemaContext(ast, path, node);
|
||||
type = typeUtil.inferMoreSpecificType(type, schemaContext);
|
||||
YType type = typeUtil.inferMoreSpecificType(_type, schemaContext);
|
||||
if (typeCollector!=null) {
|
||||
typeCollector.accept(node, type);
|
||||
}
|
||||
@@ -190,19 +191,23 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
break;
|
||||
case scalar:
|
||||
if (typeUtil.isAtomic(type)) {
|
||||
ValueParser parser = typeUtil.getValueParser(type, schemaContext);
|
||||
if (parser!=null) {
|
||||
try {
|
||||
String value = NodeUtil.asScalar(node);
|
||||
if (value!=null) {
|
||||
parser.parse(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ProblemType problemType = getProblemType(e);
|
||||
DocumentRegion region = getRegion(e, ast.getDocument(), node);
|
||||
String msg = getMessage(e);
|
||||
valueParseError(type, region, msg, problemType, getValueReplacement(e));
|
||||
}
|
||||
SchemaContextAware<ValueParser> parserProvider = typeUtil.getValueParser(type);
|
||||
if (parserProvider!=null) {
|
||||
delayedConstraints.add(() -> {
|
||||
parserProvider.safeWithContext(schemaContext).ifPresent(parser -> {
|
||||
try {
|
||||
String value = NodeUtil.asScalar(node);
|
||||
if (value!=null) {
|
||||
parser.parse(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ProblemType problemType = getProblemType(e);
|
||||
DocumentRegion region = getRegion(e, ast.getDocument(), node);
|
||||
String msg = getMessage(e);
|
||||
valueParseError(type, region, msg, problemType, getValueReplacement(e));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
expectTypeButFoundScalar(type, node);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
package org.springframework.ide.vscode.commons.yaml.reconcile;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -35,19 +35,20 @@ import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Finds symbols in a concourse document. This relies on type information cached
|
||||
* during reconcile and stored in the {@link ConcourseModel}. Therefore,
|
||||
* this handler only works if invoked after a reconcile.
|
||||
* Finds symbols in a yaml document based on type information cached
|
||||
* during reconcile and stored in the {@link ASTTypeCache}. Because
|
||||
* it relies on information computed during reconcile, this handler only
|
||||
* works if it is invoked after a reconcile.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ConcourseDocumentSymbolHandler implements DocumentSymbolHandler {
|
||||
public class TypeBasedYamlSymbolHandler implements DocumentSymbolHandler {
|
||||
|
||||
private ASTTypeCache astTypeCache;
|
||||
private Set<YType> definitionTypes;
|
||||
private SimpleTextDocumentService documents;
|
||||
|
||||
public ConcourseDocumentSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, Collection<YType> definitionTypes) {
|
||||
public TypeBasedYamlSymbolHandler(SimpleTextDocumentService documents, ASTTypeCache astTypeCache, Collection<YType> definitionTypes) {
|
||||
Assert.isTrue(!definitionTypes.isEmpty()); // If there's no interesting types then you are better of using DocumentSymbolHandler.NO_SYMBOLS
|
||||
this.documents = documents;
|
||||
this.astTypeCache = astTypeCache;
|
||||
@@ -114,7 +114,7 @@ public class YamlSchemaProblems {
|
||||
return new ReconcileProblemImpl(problemType, msg, start, end-start);
|
||||
}
|
||||
|
||||
public static ReconcileProblemImpl missingProperty(String msg, IDocument doc, Node parent, MappingNode map) {
|
||||
public static ReconcileProblemImpl missingProperty(String msg, IDocument doc, Node parent, Node map) {
|
||||
DocumentRegion underline = NodeUtil.region(doc, map);
|
||||
if (parent instanceof MappingNode) {
|
||||
for (NodeTuple prop : ((MappingNode) parent).getValue()) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Interface that can be implemented by something producing another
|
||||
* component (of some type `T`) where the returned component needs to
|
||||
@@ -19,7 +21,18 @@ package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SchemaContextAware<T> {
|
||||
T withContext(DynamicSchemaContext dc);
|
||||
T withContext(DynamicSchemaContext dc) throws Exception;
|
||||
|
||||
/**
|
||||
* Like `withContext' method, but swallows exceptions silently.
|
||||
*/
|
||||
default Optional<T> safeWithContext(DynamicSchemaContext dc) {
|
||||
try {
|
||||
return Optional.ofNullable(withContext(dc));
|
||||
} catch (Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a plain value into a {@link SchemaContextAware} that ignores the context and simply returns the value.
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.TreeSet;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReplacementQuickfix;
|
||||
@@ -52,10 +53,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;
|
||||
@@ -158,7 +157,7 @@ public class YTypeFactory {
|
||||
* YTypeFactory
|
||||
*/
|
||||
public final YTypeUtil TYPE_UTIL = new YTypeUtil() {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isSequencable(YType type) {
|
||||
return ((AbstractType)type).isSequenceable();
|
||||
@@ -210,8 +209,8 @@ public class YTypeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueParser getValueParser(YType type, DynamicSchemaContext dc) {
|
||||
return ((AbstractType)type).getParser(dc);
|
||||
public SchemaContextAware<ValueParser> getValueParser(YType type) {
|
||||
return ((AbstractType)type).getParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,6 +238,11 @@ public class YTypeFactory {
|
||||
public boolean tieredOptionalPropertyProposals() {
|
||||
return enableTieredOptionalPropertyProposals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean suggestDeprecatedProperties() {
|
||||
return suggestDeprecatedProperties;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -253,6 +257,8 @@ public class YTypeFactory {
|
||||
private List<YValueHint> hints = new ArrayList<>();
|
||||
private Map<String, YTypedProperty> cachedPropertyMap;
|
||||
private SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider;
|
||||
//TODO: SchemaContextAware now allows throwing exceptions so should be able to simplify the above to SchemaContextAware<Collection<YValueHint>>
|
||||
|
||||
private List<Constraint> constraints = new ArrayList<>(2);
|
||||
private ISubCompletionEngine customContentAssistant = null;
|
||||
|
||||
@@ -263,7 +269,7 @@ public class YTypeFactory {
|
||||
public ISubCompletionEngine getCustomContentAssistant() {
|
||||
return customContentAssistant;
|
||||
}
|
||||
|
||||
|
||||
public AbstractType setCustomContentAssistant(ISubCompletionEngine customContentAssistant) {
|
||||
this.customContentAssistant = customContentAssistant;
|
||||
return this;
|
||||
@@ -291,6 +297,7 @@ public class YTypeFactory {
|
||||
}
|
||||
|
||||
public AbstractType addHintProvider(SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider) {
|
||||
//TODO: SchemaContextAware now allows throwing exceptions so should be able to simplify the above to SchemaContextAware<Collection<YValueHint>>
|
||||
this.hintProvider = hintProvider;
|
||||
return this;
|
||||
}
|
||||
@@ -402,8 +409,8 @@ public class YTypeFactory {
|
||||
parseWith((DynamicSchemaContext dc) -> parser);
|
||||
return this;
|
||||
}
|
||||
private ValueParser getParser(DynamicSchemaContext dc) {
|
||||
return parser == null ? null : parser.withContext(dc);
|
||||
private SchemaContextAware<ValueParser> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
public AbstractType require(Constraint dynamicConstraint) {
|
||||
@@ -444,10 +451,7 @@ public class YTypeFactory {
|
||||
@Override
|
||||
public YType inferMoreSpecificType(DynamicSchemaContext dc) {
|
||||
if (dc!=null) {
|
||||
YType inferred = typeGuesser.withContext(dc);
|
||||
if (inferred!=null) {
|
||||
return inferred;
|
||||
}
|
||||
return typeGuesser.safeWithContext(dc).orElse(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -736,7 +740,7 @@ public class YTypeFactory {
|
||||
}
|
||||
|
||||
|
||||
public static class YTypedPropertyImpl implements YTypedProperty {
|
||||
public static class YTypedPropertyImpl implements YTypedProperty, Cloneable {
|
||||
|
||||
final private String name;
|
||||
final private YType type;
|
||||
@@ -793,7 +797,7 @@ public class YTypeFactory {
|
||||
this.isDeprecated = deprecationMessage!=null;
|
||||
this.deprecationMessage = deprecationMessage;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDeprecationMessage() {
|
||||
return this.deprecationMessage;
|
||||
@@ -804,7 +808,7 @@ public class YTypeFactory {
|
||||
return this.isDeprecated;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public YTypedPropertyImpl isPrimary(boolean primary) {
|
||||
this.isPrimary = primary;
|
||||
this.isRequired = primary;
|
||||
@@ -816,12 +820,19 @@ public class YTypeFactory {
|
||||
this.isRequired = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPrimary() {
|
||||
return isPrimary;
|
||||
}
|
||||
|
||||
public YTypedPropertyImpl copy() {
|
||||
try {
|
||||
return (YTypedPropertyImpl) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public YAtomicType yatomic(String name) {
|
||||
@@ -832,6 +843,10 @@ public class YTypeFactory {
|
||||
return new YTypedPropertyImpl(name, type);
|
||||
}
|
||||
|
||||
public YTypedPropertyImpl yprop(YTypedProperty prop) {
|
||||
return ((YTypedPropertyImpl)prop).copy();
|
||||
}
|
||||
|
||||
public YAtomicType yenumFromHints(String name, BiFunction<String, Collection<String>, String> errorMessageFormatter, SchemaContextAware<Collection<YValueHint>> values) {
|
||||
YAtomicType t = yatomic(name);
|
||||
t.addHintProvider((dc) -> () -> values.withContext(dc));
|
||||
@@ -847,6 +862,17 @@ public class YTypeFactory {
|
||||
return t;
|
||||
}
|
||||
|
||||
public YAtomicType yenumFromDynamicValues(String name, SchemaContextAware<Collection<String>> values) {
|
||||
return yenumFromHints(name,
|
||||
//Error message formatter:
|
||||
(parseString, validValues) -> "'"+parseString+"' is an unknown '"+name+"'. Valid values are: "+validValues,
|
||||
//Hints provider:
|
||||
(dc) ->
|
||||
hints(values.withContext(dc)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public EnumTypeBuilder yenumBuilder(String name, String... values) {
|
||||
return new EnumTypeBuilder(name, values);
|
||||
}
|
||||
@@ -916,4 +942,10 @@ public class YTypeFactory {
|
||||
return this;
|
||||
}
|
||||
|
||||
public YTypeFactory suggestDeprecatedProperties(boolean enable) {
|
||||
this.suggestDeprecatedProperties = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public interface YTypeUtil {
|
||||
YValueHint[] getHintValues(YType yType, DynamicSchemaContext dc) throws Exception;
|
||||
String niceTypeName(YType type);
|
||||
YType getKeyType(YType type);
|
||||
ValueParser getValueParser(YType type, DynamicSchemaContext dc);
|
||||
SchemaContextAware<ValueParser> getValueParser(YType type);
|
||||
|
||||
//TODO: only one of these two should be enough?
|
||||
List<YTypedProperty> getProperties(YType type);
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.yaml.schema;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.IntegerRange;
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,18 +11,25 @@
|
||||
package org.springframework.ide.vscode.commons.yaml.schema.constraints;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.EXTRA_PROPERTY;
|
||||
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.missingProperty;
|
||||
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.*;
|
||||
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.problem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache.NodeTypes;
|
||||
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.SchemaContextAware;
|
||||
@@ -31,7 +38,11 @@ import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
/**
|
||||
* Various static methods for constructing/composing {@link Constraint}s.
|
||||
@@ -39,7 +50,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class Constraints {
|
||||
|
||||
|
||||
public static Constraint requireOneOf(String... properties) {
|
||||
return new RequireOneOf(properties);
|
||||
}
|
||||
@@ -47,17 +58,28 @@ public class Constraints {
|
||||
public static Constraint requireAtMostOneOf(String... properties) {
|
||||
return new RequireOneOf(properties).allowFewer(true);
|
||||
}
|
||||
|
||||
public static Constraint requireAtLeastOneOf(String... properties) {
|
||||
return new RequireOneOf(properties).allowMultiple(true);
|
||||
}
|
||||
|
||||
|
||||
static private class RequireOneOf implements Constraint {
|
||||
|
||||
private final String[] _requiredProps;
|
||||
private boolean allowFewer = false;
|
||||
private boolean allowMultiple = false;
|
||||
|
||||
public RequireOneOf(String[] properties) {
|
||||
Assert.isLegal(properties.length>1);
|
||||
this._requiredProps = properties;
|
||||
}
|
||||
|
||||
public Constraint allowMultiple(boolean b) {
|
||||
this.allowMultiple = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Constraint allowFewer(boolean b) {
|
||||
this.allowFewer = b;
|
||||
return this;
|
||||
@@ -78,7 +100,7 @@ public class Constraints {
|
||||
problems.accept(missingProperty(
|
||||
"One of "+requiredProps+" is required for '"+type+"'", doc, parent, map));
|
||||
}
|
||||
} else if (foundPropsCount>1) {
|
||||
} else if (foundPropsCount>1 && !allowMultiple) {
|
||||
//Mark each of the found keys as a violation:
|
||||
for (NodeTuple entry : map.getValue()) {
|
||||
String key = NodeUtil.asScalar(entry.getKeyNode());
|
||||
@@ -119,7 +141,57 @@ public class Constraints {
|
||||
@Deprecated
|
||||
public static Constraint schemaContextAware(SchemaContextAware<Constraint> dispatcher) {
|
||||
return (DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) -> {
|
||||
dispatcher.withContext(dc).verify(dc, parent, node, type, problems);
|
||||
dispatcher.safeWithContext(dc).ifPresent((constraint) -> constraint.verify(dc, parent, node, type, problems));
|
||||
};
|
||||
}
|
||||
|
||||
public static Constraint mutuallyExclusive(String p1, String p2) {
|
||||
return (DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) -> {
|
||||
if (node instanceof MappingNode) {
|
||||
MappingNode map = (MappingNode) node;
|
||||
Set<String> defined = dc.getDefinedProperties();
|
||||
if (defined.contains(p1) && defined.contains(p2)) {
|
||||
for (NodeTuple tup : map.getValue()) {
|
||||
Node keyNode = tup.getKeyNode();
|
||||
String key = NodeUtil.asScalar(keyNode);
|
||||
if (p1.equals(key) || p1.equals(key)) {
|
||||
problems.accept(problem(EXTRA_PROPERTY,
|
||||
"Only one of '"+p1+"' and '"+p2+"' should be defined for '"+type+"'", keyNode
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all nodes of a given type, across the AST represent unique names.
|
||||
*/
|
||||
public static Constraint uniqueDefinition(ASTTypeCache astTypes, YType defType, ProblemType problemType) {
|
||||
return (DynamicSchemaContext dc, Node parent, Node _ignored_node, YType type, IProblemCollector problems) -> {
|
||||
NodeTypes nodeTypes = astTypes.getNodeTypes(dc.getDocument().getUri());
|
||||
if (nodeTypes!=null) {
|
||||
Collection<Node> nodes = nodeTypes.getNodes(defType);
|
||||
if (nodes!=null && !nodes.isEmpty()) {
|
||||
Multimap<String, Node> name2nodes = ArrayListMultimap.create();
|
||||
for (Node node : nodes) {
|
||||
String name = NodeUtil.asScalar(node);
|
||||
if (StringUtil.hasText(name)) {
|
||||
name2nodes.put(name, node);
|
||||
}
|
||||
}
|
||||
for (String name : name2nodes.keys()) {
|
||||
Collection<Node> nodesForName = name2nodes.get(name);
|
||||
if (nodesForName.size()>1) {
|
||||
for (Node duplicateNode : nodesForName) {
|
||||
problems.accept(YamlSchemaProblems.problem(problemType, "Duplicate '"+defType+"'", duplicateNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -326,7 +326,7 @@ public class Editor {
|
||||
return completions;
|
||||
}
|
||||
|
||||
public void assertCompletionLabels(String... expectedLabels) throws Exception {
|
||||
public List<CompletionItem> assertCompletionLabels(String... expectedLabels) throws Exception {
|
||||
StringBuilder expect = new StringBuilder();
|
||||
StringBuilder actual = new StringBuilder();
|
||||
for (String label : expectedLabels) {
|
||||
@@ -334,11 +334,13 @@ public class Editor {
|
||||
expect.append("\n");
|
||||
}
|
||||
|
||||
for (CompletionItem completion : getCompletions()) {
|
||||
List<CompletionItem> completions;
|
||||
for (CompletionItem completion : completions = getCompletions()) {
|
||||
actual.append(completion.getLabel());
|
||||
actual.append("\n");
|
||||
}
|
||||
assertEquals(expect.toString(), actual.toString());
|
||||
return completions;
|
||||
}
|
||||
|
||||
public void assertContainsCompletions(String... expectTextAfter) throws Exception {
|
||||
@@ -383,6 +385,32 @@ public class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
public void assertContextualCompletions(LanguageId language, Predicate<CompletionItem> isInteresting, String textBefore, String... textAfter) throws Exception {
|
||||
Editor editor = harness.newEditor(language, this.getText());
|
||||
editor.reconcile(); //this ensures the conText is parsed and its AST is cached (will be used for
|
||||
//dynamic CA when the conText + textBefore is not parsable.
|
||||
|
||||
textBefore = replaceSelection(textBefore);
|
||||
textAfter = Arrays.stream(textAfter)
|
||||
.map((String t) -> replaceSelection(t))
|
||||
.collect(Collectors.toList()).toArray(new String[0]);
|
||||
editor.setText(textBefore);
|
||||
editor.assertCompletions(isInteresting, textAfter);
|
||||
}
|
||||
|
||||
public void assertContextualCompletions(String textBefore, String... textAfter) throws Exception {
|
||||
assertContextualCompletions(getLanguageId(), (x) -> true, textBefore, textAfter);
|
||||
}
|
||||
|
||||
private String replaceSelection(String replacement) {
|
||||
try {
|
||||
String text = getRawText();
|
||||
return text.substring(0, selectionStart) + replacement + text.substring(selectionEnd);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void apply(CompletionItem completion) throws Exception {
|
||||
completion = harness.resolveCompletionItem(completion);
|
||||
TextEdit edit = completion.getTextEdit();
|
||||
|
||||
@@ -14,8 +14,11 @@ package org.springframework.ide.vscode.languageserver.testharness;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
@@ -80,6 +83,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.LanguageServer
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.IOUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
@@ -569,4 +573,16 @@ public class LanguageServerHarness {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new editor and populate contents from a file found on the (test) classpath.
|
||||
*/
|
||||
public Editor newEditorFromClasspath(String resourcePath) throws Exception {
|
||||
try (InputStream is = LanguageServerHarness.class.getResourceAsStream(resourcePath)) {
|
||||
if (is==null) {
|
||||
fail("Couldn't find the resource: "+resourcePath);
|
||||
}
|
||||
return newEditor(IOUtil.toString(is));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
</modules>
|
||||
|
||||
<repositories>
|
||||
<!-- Note: avoid using this repo, it can block project builds for long time.
|
||||
<repository>
|
||||
<id>lsp4j-snapshots-repo</id>
|
||||
<name>lsp4j-snapshots-repo</name>
|
||||
@@ -34,6 +35,7 @@
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
-->
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
@@ -75,9 +77,10 @@
|
||||
<assertj-version>3.5.2</assertj-version>
|
||||
<slf4j-version>1.7.22</slf4j-version>
|
||||
<guava-version>19.0</guava-version>
|
||||
<mockito-version>1.10.19</mockito-version>
|
||||
<jackson-2-version>2.5.0</jackson-2-version>
|
||||
<jersey-2-version>2.10</jersey-2-version>
|
||||
<lsp4j-version>0.2.0-SNAPSHOT</lsp4j-version>
|
||||
<lsp4j-version>0.2.1</lsp4j-version>
|
||||
<!-- NOTE: Reactor version must match version used by the CF client -->
|
||||
<reactor-version>3.0.5.RELEASE</reactor-version>
|
||||
<reactor-netty>0.6.0.RELEASE</reactor-netty>
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
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.schema.YType;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngi
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.commons.yaml.hover.YamlHoverInfoProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.quickfix.YamlQuickfixes;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.TypeBasedYamlSymbolHandler;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaBasedReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
@@ -74,7 +75,7 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
|
||||
reconcileEngine.setTypeCollector(models.getAstTypeCache());
|
||||
|
||||
this.symbolHandler = CollectionUtil.hasElements(definitionTypes)
|
||||
? new ConcourseDocumentSymbolHandler(documents, models.getAstTypeCache(), definitionTypes)
|
||||
? new TypeBasedYamlSymbolHandler(documents, models.getAstTypeCache(), definitionTypes)
|
||||
: DocumentSymbolHandler.NO_SYMBOLS;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ import org.springframework.ide.vscode.commons.yaml.path.NodeCursor;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlTraversal;
|
||||
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.reconcile.ASTTypeCache.NodeTypes;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
@@ -51,7 +53,6 @@ import org.springframework.ide.vscode.commons.yaml.schema.constraints.Constraint
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
|
||||
import org.springframework.ide.vscode.commons.yaml.util.Streams;
|
||||
import org.springframework.ide.vscode.concourse.ASTTypeCache.NodeTypes;
|
||||
import org.springframework.ide.vscode.concourse.util.StaleFallbackCache;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
@@ -473,7 +473,7 @@ public class ConcourseEditorTest {
|
||||
editor = harness.newEditor("jo<*>");
|
||||
editor.assertCompletions(
|
||||
"jobs:\n"+
|
||||
"- <*>"
|
||||
"- name: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -486,19 +486,19 @@ public class ConcourseEditorTest {
|
||||
"- name: <*>"
|
||||
, // --------------
|
||||
"jobs:\n" +
|
||||
"- <*>"
|
||||
"- name: <*>"
|
||||
, // ---------------
|
||||
"resource_types:\n" +
|
||||
"- <*>"
|
||||
"- name: <*>"
|
||||
, // ---------------
|
||||
"resources:\n"+
|
||||
"- <*>"
|
||||
"- name: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor("rety<*>");
|
||||
editor.assertCompletions(
|
||||
"resource_types:\n" +
|
||||
"- <*>"
|
||||
"- name: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.10.19</version>
|
||||
<version>${mockito-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
Reference in New Issue
Block a user