Skip to content

Commit

Permalink
Implemented writeYaml step
Browse files Browse the repository at this point in the history
  • Loading branch information
witokondoria committed Jun 16, 2017
1 parent c0690fd commit ef1a671
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* `readProperties` - Read [java properties](https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html) from files in the workspace or text. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/conf/ReadPropertiesStep/help.html))
* `readManifest` - Read a [Jar Manifest](https://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#JAR_Manifest). ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/conf/mf/ReadManifestStep/help.html))
* `readYaml` - Read [YAML](http://yaml.org) from files in the workspace or text. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/conf/ReadYamlStep/help.html))
* `writeYaml` - Write a [YAML](http://yaml.org) file from an object. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/conf/WriteYamlStep/help.html))
* `readJSON` - Read [JSON](http://www.json.org/json-it.html) from files in the workspace or text. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/json/ReadJSONStep/help.html))
* `writeJSON` - Write a [JSON](http://www.json.org/json-it.html) object to a files in the workspace. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/json/WriteJSONStep/help.html))

Expand Down
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,11 @@
<url>https://github.com/jenkinsci/pipeline-utility-steps-plugin</url>
<tag>HEAD</tag>
</scm>

<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Javier DELGADO
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package org.jenkinsci.plugins.pipeline.utility.steps.conf;

import hudson.Extension;
import hudson.FilePath;
import hudson.model.TaskListener;
import org.jenkinsci.plugins.pipeline.utility.steps.shaded.org.yaml.snakeyaml.DumperOptions;
import org.jenkinsci.plugins.pipeline.utility.steps.shaded.org.yaml.snakeyaml.Yaml;
import org.jenkinsci.plugins.workflow.steps.*;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.io.*;
import java.nio.file.FileAlreadyExistsException;

import static org.apache.commons.lang.StringUtils.isBlank;

/**
* Writes a yaml file from the workspace.
*
* @author Javier DELGADO &lt;witokondoria@gmail.com&gt;.
*/
public class WriteYamlStep extends AbstractStepImpl {

private String file;
private Object data;

@DataBoundConstructor
public WriteYamlStep(@Nonnull String file, @Nonnull Object data) {
if (file == null) {
throw new IllegalArgumentException("file parameter must be provided to writeYaml");
}
this.file = file;
if (data == null) {
throw new IllegalArgumentException("data parameter must be provided to writeYaml");
}
this.data = data;
}

/**
* Name of the yaml file to write.
*
* @return file name
*/
public String getFile() {
return file;
}

/**
* Name of the yaml file to write.
*
* @param file file name
*/
public void setFile(String file) {
this.file = file;
}

/**
* An Object containing data to be saved.
*
* @return data to save as yaml
*/
public Object getData() {
return data;
}

/**
* An Object representing the elements to write.
*
* @param data to parse
*/
public void setData(Object data) {
this.data = data;
}

@Extension
public static class DescriptorImpl extends AbstractStepDescriptorImpl {

public DescriptorImpl() {
super(Execution.class);
}

@Override
public String getFunctionName() {
return "writeYaml";
}

@Override
public String getDisplayName() {
return "Write a yaml from an object.";
}
}

public static class Execution extends AbstractSynchronousNonBlockingStepExecution<Void> {
private static final long serialVersionUID = 1L;

@StepContextParameter
private transient TaskListener listener;

@StepContextParameter
private transient FilePath ws;

@Inject
private transient WriteYamlStep step;

@Override
protected Void run () throws Exception {
FilePath path = null;
if (!isBlank(step.getFile())) {
path = ws.child(step.getFile());
if (path.exists()) {
throw new FileAlreadyExistsException(path.getRemote() + " already exist.");
}
if (path.isDirectory()) {
throw new FileNotFoundException(path.getRemote() + " is a directory.");
}
}

DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);

try (OutputStreamWriter writer = new OutputStreamWriter(path.write())) {
yaml.dump (step.getData(), writer);
}

return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ The MIT License (MIT)
~
~ Copyright (c) 2017 Javier DELGADO.
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry title="${%file.title}" field="file" description="${%file.description}">
<f:textbox />
</f:entry>

<f:entry title="${%text.title}" field="text" description="${%text.description}">
<f:textbox />
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
file.title=File
file.description=File to save as YAML
text.title=Data
text.description=Object to serialize yo YAML
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!--
~ The MIT License (MIT)
~
~ Copyright (c) 2017 Javier DELGADO.
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
-->
<p>
Writes a yaml file in the current working directory from an Object or a String.
It uses <a href="https://bitbucket.org/asomov/snakeyaml" target="_blank">SnakeYAML</a> as YAML processor.
The call will fail if the file already exists.
</p>
<strong>Fields:</strong>
<ul>
<li>
<code>file</code>:
Mandatory path to a file in the workspace to write the YAML datas to.
</li>
<li>
<code>data</code>:
A Mandatory Object containing the data to be serialized.
</li>
</ul>
<p>
<strong>Examples:</strong><br/>
<code>
<pre>
def amap = ['something': 'my datas',
'size': 3,
'isEmpty': false]

writeYaml file: 'datas.yaml', data: amap
def read = readYaml file: 'datas.yaml'

assert read.something == 'my datas'
assert read.size == 3
assert read.isEmpty == false
</pre>
</code>
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.jenkinsci.plugins.pipeline.utility.steps.conf;

import hudson.model.Label;
import hudson.model.Result;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.JenkinsRule;

public class WriteYamlStepTest {
@Rule
public JenkinsRule j = new JenkinsRule();
@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Before
public void setup() throws Exception {
j.createOnlineSlave(Label.get("slaves"));
}

@Test
public void writeInvalidMap() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"def l = [] \n" +
"node('slaves') {\n" +
" writeYaml file: 'test', data: /['a': ]/ \n" +
" def yml = readYaml file: 'test' \n" +
" assert yml == /['a': ]/ \n" +
"}",
true));
WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
}

@Test
public void writeArbitraryObject() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"def l = [] \n" +
"node('slaves') {\n" +
" writeYaml file: 'test', data: new Date() \n" +
" def yml = readYaml file: 'test' \n" +
" assert yml =~ /2\\d{3}/ \n" +
"}",
true));
WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
}

@Test
public void writeMapObject() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "map");
p.setDefinition(new CpsFlowDefinition(
"node('slaves') {\n" +
" writeYaml file: 'test', data: ['a': 1, 'b': 2] \n" +
" def yml = readYaml file: 'test' \n" +
" assert yml == ['a' : 1, 'b': 2] \n" +
"}",
true));
WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));

}

@Test
public void writeListObjectAndRead() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "list");
p.setDefinition(new CpsFlowDefinition(
"node('slaves') {\n" +
" writeYaml file: 'test', data: ['a', 'b', 'c'] \n" +
" def yml = readYaml file: 'test' \n" +
" assert yml == ['a','b','c'] \n"+
"}",
true));
WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
}

@Test
public void writeExistingFile() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "list");
p.setDefinition(new CpsFlowDefinition(
"node('slaves') {\n" +
" sh 'touch test' \n" +
" writeYaml file: 'test', data: ['a', 'b', 'c'] \n" +
"}",
true));
WorkflowRun b = j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
j.assertLogContains("FileAlreadyExistsException", b);
}

@Test
public void writeNoData() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("node('slaves') {\n" + " writeYaml file: 'some' \n" + "}", true));
WorkflowRun run = j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
j.assertLogContains("data parameter must be provided to writeYaml", run);
}

@Test
public void writeNoFile() throws Exception {
WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("node('slaves') {\n" + " writeYaml data: 'some' \n" + "}", true));
WorkflowRun run = j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
j.assertLogContains("file parameter must be provided to writeYaml", run);
}
}

0 comments on commit ef1a671

Please sign in to comment.