Support for semver resource

This commit is contained in:
Kris De Volder
2017-01-30 13:49:59 -08:00
parent 3702cc556f
commit 42239fd952
23 changed files with 469 additions and 22 deletions

View File

@@ -29,6 +29,7 @@ import org.springframework.ide.vscode.commons.util.EnumValueParser;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
@@ -132,7 +133,13 @@ public class YTypeFactory {
@Override
public YType inferMoreSpecificType(YType type, DynamicSchemaContext schemaContext) {
return ((AbstractType)type).inferMoreSpecificType(schemaContext);
YType better = ((AbstractType)type).inferMoreSpecificType(schemaContext);
while (better!=null && better!=type) {
type = better;
better = ((AbstractType)type).inferMoreSpecificType(schemaContext);
}
//Can only get here if either 'better' is null or better==type
return type;
}
@Override
@@ -313,7 +320,10 @@ public class YTypeFactory {
private final SchemaContextAware<YType> typeGuesser;
private final String name;
boolean treatAsAtomic = false;
private boolean isAtomic = true;
private boolean isMap = true;
private boolean isBean = true;
private boolean isSeq = true;
public YContextSensitive(String name, SchemaContextAware<YType> typeGuesser) {
this.name = name;
@@ -333,17 +343,22 @@ public class YTypeFactory {
@Override
public boolean isAtomic() {
return true;
return isAtomic;
}
@Override
public boolean isSequenceable() {
return !treatAsAtomic;
return isSeq;
}
@Override
public boolean isMap() {
return !treatAsAtomic;
return isMap;
}
@Override
public boolean isBean() {
return isBean;
}
@Override
@@ -358,8 +373,19 @@ public class YTypeFactory {
* If set to true, then it is treated as strictly atomic type instead (i.e it isn't valid to
* use a map or sequence for its value).
*/
public YContextSensitive treatAsAtomic(boolean isAtomic) {
this.treatAsAtomic = isAtomic;
public AbstractType treatAsAtomic() {
this.isAtomic = true;
this.isMap = false;
this.isBean = false;
this.isSeq = false;
return this;
}
public AbstractType treatAsBean() {
this.isAtomic = false;
this.isMap = false;
this.isBean = true;
this.isSeq = false;
return this;
}

View File

@@ -16,7 +16,6 @@ import java.util.stream.Collectors;
import org.springframework.ide.vscode.commons.util.MimeTypes;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.util.ValueParsers;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
@@ -30,15 +29,14 @@ import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicTy
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanUnionType;
import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl;
import reactor.core.publisher.Flux;
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.YValueHint;
import org.springframework.ide.vscode.commons.yaml.schema.YamlSchema;
import org.yaml.snakeyaml.nodes.Node;
import reactor.core.publisher.Flux;
/**
* @author Kris De Volder
*/
@@ -108,7 +106,24 @@ public class PipelineYmlSchema implements YamlSchema {
private final ResourceTypeRegistry resourceTypes = new ResourceTypeRegistry();
private final ConcourseModel models;
public final YType t_semver = f.yatomic("Semver")
.parseWith(ValueParsers.NE_STRING); //TODO: use real semver parser.
public final YType t_s3_region = f.yenum("S3Region",
//See: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html
"us-west-1", "us-west-2",
"ca-central-1", "EU", "eu-west-1",
"eu-west-2", "eu-central-1",
"ap-south-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ap-northeast-2",
"sa-east-1",
"us-east-2"
);
public PipelineYmlSchema(ConcourseModel models) {
this.models = models;
TYPE_UTIL = f.TYPE_UTIL;
// define schema types
@@ -205,7 +220,7 @@ public class PipelineYmlSchema implements YamlSchema {
return t_resource_name;
}
})
.treatAsAtomic(true)
.treatAsAtomic()
.parseWith(ValueParsers.NE_STRING);
YBeanType getStep = f.ybean("GetStep");
@@ -381,16 +396,6 @@ public class PipelineYmlSchema implements YamlSchema {
}
//s3
{
YType t_s3_region = f.yenum("S3Region",
//See: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html
"us-west-1", "us-west-2",
"ca-central-1", "EU", "eu-west-1",
"eu-west-2", "eu-central-1",
"ap-south-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ap-northeast-2",
"sa-east-1",
"us-east-2"
);
YType t_canned_acl = f.yenum("S3CannedAcl",
//See http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
"private", "public-read", "public-read-write", "aws-exec-read",
@@ -449,7 +454,70 @@ public class PipelineYmlSchema implements YamlSchema {
resourceTypes.def("pool", source, get, put);
}
//semver
{
AbstractType git_source = f.ybean("GitSemverSource");
addProp(git_source, "uri", t_ne_string).isRequired(true);
addProp(git_source, "branch", t_ne_string).isRequired(true);
addProp(git_source, "file", t_ne_string).isRequired(true);
addProp(git_source, "private_key", t_ne_string);
addProp(git_source, "username", t_ne_string);
addProp(git_source, "password", t_ne_string);
addProp(git_source, "git_user", t_ne_string);
AbstractType s3_source = f.ybean("S3SemverSource");
addProp(s3_source, "bucket", t_ne_string).isRequired(true);
addProp(s3_source, "key", t_ne_string).isRequired(true);
addProp(s3_source, "access_key_id", t_ne_string).isRequired(true);
addProp(s3_source, "secret_access_key", t_ne_string).isRequired(true);
addProp(s3_source, "region_name", t_s3_region);
addProp(s3_source, "endpoint", t_ne_string);
addProp(s3_source, "disable_ssl", t_boolean);
AbstractType swift_source = f.ybean("SwiftSemverSource");
addProp(swift_source, "openstack", t_any).isRequired(true);
AbstractType[] driverSpecificSources = {
git_source, s3_source, swift_source
};
AbstractType source = f.contextAware("SemverSource", (dc) -> {
switch (getSemverDriverName(dc)) {
case "git":
return git_source;
case "s3":
return s3_source;
case "swift":
return swift_source;
default:
return null;
}
}).treatAsBean();
addProp(source, "initial_version", t_semver);
addProp(source, "driver", f.yenum("SemverDriver", "git", "s3", "swift"));
for (AbstractType s : driverSpecificSources) {
for (YTypedProperty p : source.getProperties()) {
s.addProperty(p);
}
}
AbstractType get = f.ybean("SemverGetParams");
addProp(get, "bump", f.yenum("SemverBump", "major", "minor", "patch", "final"));
addProp(get, "pre", t_ne_string);
AbstractType put = f.ybean("SemverPutParams");
for (YTypedProperty p : get.getProperties()) {
put.addProperty(p);
}
addProp(put, "file", t_ne_string);
resourceTypes.def("semver", source, get, put);
}
}
private String getSemverDriverName(DynamicSchemaContext dc) {
String driver = getSiblingPropertyValue(dc, "driver");
return driver!=null ? driver : "s3";
}
private String getResourceType(String resourceNameProp, ConcourseModel models, DynamicSchemaContext dc) {
@@ -471,6 +539,17 @@ public class PipelineYmlSchema implements YamlSchema {
return NodeUtil.asScalar(getParentPropertyNode(propName, models, dc));
}
private String getSiblingPropertyValue(DynamicSchemaContext dc, String propName) {
YamlPath path = dc.getPath();
if (path!=null) {
YamlFileAST root = models.getSafeAst(dc.getDocument());
if (root!=null) {
return NodeUtil.asScalar(path.append(YamlPathSegment.valueAt(propName)).traverseToNode(root));
}
}
return null;
}
private Node getParentPropertyNode(String propName, ConcourseModel models, DynamicSchemaContext dc) {
YamlPath path = dc.getPath();
if (path!=null) {

View File

@@ -0,0 +1 @@
*Required.* The branch the file lives on.

View File

@@ -0,0 +1 @@
*Required.* The name of the file in the repository.

View File

@@ -0,0 +1,2 @@
*Optional.* The git identity to use when pushing to the
repository support RFC 5322 address of the form "Gogh Fir \<gf@example.com\>" or "foo@example.com".

View File

@@ -0,0 +1 @@
*Optional.* Password for HTTP(S) auth when pulling/pushing.

View File

@@ -0,0 +1 @@
*Optional.* The SSH private key to use when pulling from/pushing to to the repository.

View File

@@ -0,0 +1 @@
*Required.* The repository URL.

View File

@@ -0,0 +1,3 @@
*Optional.* Username for HTTP(S) auth when pulling/pushing.
This is needed when only HTTP/HTTPS protocol for git is available (which does not support private key auth)
and auth is required.

View File

@@ -0,0 +1,2 @@
*Required.* The AWS access key to use when accessing the
bucket.

View File

@@ -0,0 +1 @@
*Required.* The name of the bucket.

View File

@@ -0,0 +1 @@
*Optional.* Disable SSL for the endpoint, useful for S3 compatible providers without SSL.

View File

@@ -0,0 +1 @@
*Optional.* Custom endpoint for using S3 compatible provider.

View File

@@ -0,0 +1,2 @@
*Required.* The key to use for the object in the bucket tracking
the version.

View File

@@ -0,0 +1 @@
*Optional. Default `us-east-1`.* The region the bucket is in.

View File

@@ -0,0 +1,2 @@
*Required.* The AWS secret key to use when accessing
the bucket.

View File

@@ -0,0 +1,7 @@
*Optional.* Bump the version number semantically. The value must
be one of:
* `major`: Bump the major version number, e.g. `1.0.0` -> `2.0.0`.
* `minor`: Bump the minor version number, e.g. `0.1.0` -> `0.2.0`.
* `patch`: Bump the patch version number, e.g. `0.0.1` -> `0.0.2`.
* `final`: Promote the version to a final version, e.g. `1.0.0-rc.1` -> `1.0.0`.

View File

@@ -0,0 +1,8 @@
*Optional.* When bumping, bump to a prerelease (e.g. `rc` or
`alpha`), or bump an existing prerelease.
If present, and the version is already a prerelease matching this value,
its number is bumped. If the version is already a prerelease of another
type, (e.g. `alpha` vs. `beta`), the type is switched and the prerelease
version is reset to `1`. If the version is *not* already a pre-release, then
`pre` is added, starting at `1`.

View File

@@ -0,0 +1 @@
Optional.* Path to a file containing the version number to set.

View File

@@ -0,0 +1,2 @@
*Optional. Default `s3`.* The driver to use for tracking the
version. Determines where the version is stored.

View File

@@ -0,0 +1,2 @@
*Optional.* The version number to use when bootstrapping, i.e. when
there is not a version number present in the source.

View File

@@ -0,0 +1,14 @@
*Required.* All openstack configuration must go under this key.
* `container`: *Required.* The name of the container.
* `item_name`: *Required.* The item name to use for the object in the container tracking
the version.
* `region_name`: *Required.* The region the container is in.
* `identity_endpoint`, `username`, `user_id`, `password`, `api_key`, `domain_id`, `domain_name`, `tenant_id`, `tenant_name`, `allow_reauth`, `token_id`: See below
The swift driver uses [gophercloud](http://gophercloud.io/docs/) to handle interacting
with OpenStack. All OpenStack Identity versions are supported through this library. The
Authentication properties will pass through to it. For detailed information about the
individual parameters, see https://github.com/rackspace/gophercloud/blob/master/auth_options.go

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.concourse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.springframework.ide.vscode.languageserver.testharness.TestAsserts.assertContains;
import java.io.InputStream;
@@ -1603,6 +1604,293 @@ public class ConcourseEditorTest {
editor.assertHoverContains("remove", "remove the given lock from the pool");
}
@Test public void semverResourceSourceReconcileAtomNotAllowed() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source: an-atom"
);
editor.assertProblems("an-atom|Expecting a 'Map'");
}
@Test public void semverResourceSourceReconcileRequiredProps() throws Exception {
Editor editor;
//required props for s3 driver
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" driver: s3"
);
editor.assertProblems(
"driver: s3|[access_key_id, bucket, key, secret_access_key] are required"
);
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source: {}"
);
editor.assertProblems(
"{}|[access_key_id, bucket, key, secret_access_key] are required"
);
// required props for git driver
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" driver: git"
);
editor.assertProblems(
"driver: git|[branch, file, uri] are required"
);
//required props for swift driver
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" driver: swift"
);
editor.assertProblems(
"driver: swift|'openstack' is required"
);
}
@Test public void semverResourceSourceBadDriver() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" driver: bad-driver"
);
editor.assertProblems("bad-driver|'SemverDriver'");
}
@Test public void semverGitResourceSourceContentAssist() throws Exception {
String conText =
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
"<*>";
assertContextualCompletions(conText,
" driver: git\n" +
" <*>"
, // ==>
" driver: git\n" +
" branch: <*>"
,
" driver: git\n" +
" file: <*>"
,
" driver: git\n" +
" git_user: <*>"
,
" driver: git\n" +
" initial_version: <*>"
,
" driver: git\n" +
" password: <*>"
,
" driver: git\n" +
" private_key: <*>"
,
" driver: git\n" +
" uri: <*>"
,
" driver: git\n" +
" username: <*>"
,
" driver: git<*>"
);
}
@Test public void semverGitResourceSourceReconcileAndHovers() throws Exception {
Editor editor;
// required props for git driver
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" initial_version: not-a-version\n" + //TODO: should be marked as a error but isn't yet.
" driver: git\n" +
" uri: git@github.com:concourse/concourse.git\n" +
" branch: version\n" +
" file: version\n" +
" private_key: {{concourse-repo-private-key}}\n" +
" username: jsmith\n" +
" password: s3cre$t\n" +
" git_user: jsmith@mailhost.com\n" +
" bogus: bad"
);
editor.assertProblems(
"bogus|Unknown property"
);
editor.assertHoverContains("initial_version", "version number to use when bootstrapping");
editor.assertHoverContains("driver", "The driver to use");
editor.assertHoverContains("uri", "The repository URL");
editor.assertHoverContains("branch", "The branch the file lives on");
editor.assertHoverContains("file", "The name of the file");
editor.assertHoverContains("private_key", "The SSH private key");
editor.assertHoverContains("username", "Username for HTTP(S) auth");
editor.assertHoverContains("password", "Password for HTTP(S) auth");
editor.assertHoverContains("git_user", "The git identity to use");
}
@Test public void semverS3ResourceSourceReconcileAndHovers() throws Exception {
Editor editor;
//without explicit 'driver'... should assume s3 by default
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" initial_version: 1.2.3\n" +
" bucket: the-bucket\n" +
" key: object-key\n" +
" access_key_id: aws-access-key\n" +
" secret_access_key: aws-access-key\n" +
" region_name: bogus-region\n" +
" endpoint: https://blah.com/blah\n" +
" disable_ssl: no-use-ssl\n" +
" bogus-prop: bad"
);
editor.assertProblems(
"bogus-region|'S3Region'",
"no-use-ssl|'boolean'",
"bogus-prop|Unknown property"
);
//with explicit 'driver: s3'
editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" initial_version: 1.2.3\n" +
" driver: s3\n" +
" bucket: the-bucket\n" +
" key: object-key\n" +
" access_key_id: aws-access-key\n" +
" secret_access_key: aws-access-key\n" +
" region_name: bogus-region\n" +
" endpoint: https://blah.com/blah\n" +
" disable_ssl: no-use-ssl\n" +
" bogus-prop: bad"
);
editor.assertProblems(
"bogus-region|'S3Region'",
"no-use-ssl|'boolean'",
"bogus-prop|Unknown property"
);
editor.assertHoverContains("initial_version", "version number to use when bootstrapping");
editor.assertHoverContains("driver", "The driver to use");
editor.assertHoverContains("bucket", "The name of the bucket");
editor.assertHoverContains("key", "The key to use for the object");
editor.assertHoverContains("access_key_id", "The AWS access key to");
editor.assertHoverContains("secret_access_key", "The AWS secret key to");
editor.assertHoverContains("region_name", "The region the bucket is in");
editor.assertHoverContains("endpoint", "Custom endpoint for using S3");
editor.assertHoverContains("disable_ssl", "Disable SSL for the endpoint");
}
@Test public void semverSwiftResourceSourceReconcileAndHovers() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" initial_version: 1.2.3\n" +
" driver: swift\n" +
" openstack:\n" +
" container: nice-container\n" +
" item_name: flubber-blub\n" +
" region_name: us-west-1\n"
);
editor.assertProblems(/*NONE*/);
editor.assertHoverContains("openstack", "All openstack configuration");
}
@Test public void semverResourceGetParamsReconcileAndHovers() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" initial_version: 1.2.3\n" +
" driver: swift\n" +
" openstack: whatever\n" +
"jobs:\n" +
"- name: a-job\n" +
" plan:\n" +
" - get: version\n" +
" params:\n" +
" bump: what-to-bump\n" +
" pre: beta\n" +
" bogus: bad\n"
);
editor.assertProblems(
"what-to-bump|[final, major, minor, patch]",
"bogus|Unknown property"
);
editor.assertHoverContains("bump", "Bump the version number");
editor.assertHoverContains("pre", "bump to a prerelease");
}
@Test public void semverPutParamsReconcileAndHovers() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: version\n" +
" type: semver\n" +
" source:\n" +
" initial_version: 1.2.3\n" +
" driver: swift\n" +
" openstack: whatever\n" +
"jobs:\n" +
"- name: a-job\n" +
" plan:\n" +
" - put: version\n" +
" params:\n" +
" file: version-file\n" +
" bump: what-to-bump\n" +
" pre: alpha\n" +
" bogus-one: bad\n" +
" get_params:\n" +
" file: not-expected-here\n" +
" bump: what-to-get-bump\n" +
" pre: beta\n" +
" bogus-two: bad\n"
);
editor.assertProblems(
"what-to-bump|[final, major, minor, patch]",
"bogus-one|Unknown property",
"file|Unknown property",
"what-to-get-bump|[final, major, minor, patch]",
"bogus-two|Unknown property"
);
editor.assertHoverContains("file", 1, "Path to a file containing the version number");
editor.assertHoverContains("bump", 1, "Bump the version number");
editor.assertHoverContains("bump", 2, "Bump the version number");
editor.assertHoverContains("pre", 1, "bump to a prerelease");
editor.assertHoverContains("pre", 2, "bump to a prerelease");
}
@Test public void reconcileExplicitResourceAttributeInPutStep() throws Exception {
//See: https://www.pivotaltracker.com/story/show/138568839
Editor editor;