Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions integrations/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
<properties>
<!-- no need to be deployed during release, this is a test-only module -->
<maven.deploy.skip>true</maven.deploy.skip>
<jenkins.baseline>2.516</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.3</jenkins.version>
</properties>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@

@Override
public String getDescription() {
return "Reload your configuration or update configuration source";
return "Reload your configuration or update configuration source.";
}

/**
Expand Down Expand Up @@ -218,38 +218,51 @@
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String newSource = request.getParameter("_.newSource");
String normalizedSource = Util.fixEmptyAndTrim(newSource);
List<String> candidateSources = new ArrayList<>();
for (String candidateSource : inputToCandidateSources(normalizedSource)) {
File file = new File(candidateSource);
if (file.exists() || ConfigurationAsCode.isSupportedURI(candidateSource)) {
candidateSources.add(candidateSource);
} else {
LOGGER.log(Level.WARNING, "Source {0} could not be applied", candidateSource);
// todo: show message in UI
try {
String newSource = request.getParameter("_.newSource");
String normalizedSource = Util.fixEmptyAndTrim(newSource);
List<String> candidateSources = new ArrayList<>();
for (String candidateSource : inputToCandidateSources(normalizedSource)) {
File file = new File(candidateSource);
if (file.exists() || ConfigurationAsCode.isSupportedURI(candidateSource)) {
candidateSources.add(candidateSource);
} else {
LOGGER.log(Level.WARNING, "Source {0} could not be applied", candidateSource);
// todo: show message in UI
}
}
}
if (!candidateSources.isEmpty()) {
List<YamlSource> candidates = getConfigFromSources(candidateSources);
if (canApplyFrom(candidates)) {
sources = candidateSources;
configureWith(getConfigFromSources(getSources()));
CasCGlobalConfig config = GlobalConfiguration.all().get(CasCGlobalConfig.class);
if (config != null) {
config.setConfigurationPath(normalizedSource);
config.save();
if (!candidateSources.isEmpty()) {
List<YamlSource> candidates = getConfigFromSources(candidateSources);
if (canApplyFrom(candidates)) {
sources = candidateSources;
configureWith(getConfigFromSources(getSources()));
CasCGlobalConfig config = GlobalConfiguration.all().get(CasCGlobalConfig.class);
if (config != null) {
config.setConfigurationPath(normalizedSource);
config.save();
}
LOGGER.log(Level.FINE, "Replace configuration with: " + normalizedSource);
} else {
LOGGER.log(Level.WARNING, "Provided sources could not be applied");
// todo: show message in UI
}
LOGGER.log(Level.FINE, "Replace configuration with: " + normalizedSource);
} else {
LOGGER.log(Level.WARNING, "Provided sources could not be applied");
// todo: show message in UI
LOGGER.log(Level.FINE, "No such source exists, applying default");
// May be do nothing instead?
configure();
}
} else {
LOGGER.log(Level.FINE, "No such source exists, applying default");
// May be do nothing instead?
configure();
} catch (ConfiguratorException e) {
LOGGER.log(Level.SEVERE, "Failed to reload configuration", e);

Throwable throwableCause = e.getCause();
if (throwableCause instanceof ConfiguratorException cause) {
handleExceptionOnReloading(request, response, cause);
} else {
handleExceptionOnReloading(request, response, e);
}
return;
}

response.sendRedirect("");
}

Expand Down Expand Up @@ -556,7 +569,23 @@
ByteArrayOutputStream out = new ByteArrayOutputStream();
export(out);

req.setAttribute("export", out.toString(StandardCharsets.UTF_8.name()));
req.setAttribute("viewExport", new ManagementLink() {
Comment thread
timja marked this conversation as resolved.
@Override
public String getIconFileName() {
return "";
}

// TODO - FIX - EXTREMELY HACKY - couldn't expose a public method for some reason

Check warning on line 578 in plugin/src/main/java/io/jenkins/plugins/casc/ConfigurationAsCode.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: - FIX - EXTREMELY HACKY - couldn't expose a public method for some reason
@Override
public String getUrlName() {
return out.toString(StandardCharsets.UTF_8);
}

@Override
public String getDisplayName() {
return "Export configuration";
}
});
req.getView(this, "viewExport.jelly").forward(req, res);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,93 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form" xmlns:i="jelly:fmt" xmlns:st="jelly:stapler">
<l:layout type="one-column" title="${%Configuration as Code}" permissions="${app.MANAGE_AND_SYSTEM_READ}">
<l:main-panel>
<l:app-bar title="${%Configuration as Code}">
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:f="/lib/form" xmlns:i="jelly:fmt"
xmlns:t="/lib/hudson">
<l:settings-subpage permissions="${app.MANAGE_AND_SYSTEM_READ}" header="${null}">
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why pass header=null? you can just pass your own header through with a j:set ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dialog wasn't working in the <l:notice /> with a null header, so I moved it inside the subpage.

<l:dialog title="${%Apply Configuration as Code file}" hash="new">
<l:isAdmin>
<f:form method="post" action="replace" name="replace">
<input type="hidden" name="${h.getCrumbRequestField()}" value="${h.getCrumb(request2)}"/>
<f:entry title="${%Path or URL}" field="newSource" class="jenkins-form-item--small">
<f:textbox checkUrl="checkNewSource" checkDependsOn="newSource"/>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@janfaracik most likely the cause of the ATH failure -
https://ci.jenkins.io/job/Core/job/acceptance-test-harness/job/master/1872/testReport/junit/plugins/ConfigurationAsCodeTest/latest_linux_jdk25_firefox_split7___loadAndReload/

org.openqa.selenium.NoSuchElementException: Unable to locate By.cssSelector: [path='/newSource'] in http://mvn:35985/configuration-as-code/
	at org.jenkinsci.test.acceptance.po.CapybaraPortingLayerImpl.find(CapybaraPortingLayerImpl.java:206)
	at org.jenkinsci.test.acceptance.po.Control.resolve(Control.java:58)
	at org.jenkinsci.test.acceptance.po.Control.set(Control.java:162)
	at org.jenkinsci.test.acceptance.plugins.configuration_as_code.JcascManage.configure(JcascManage.java:46)
	at plugins.ConfigurationAsCodeTest.loadAndReload(ConfigurationAsCodeTest.java:46)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</f:entry>
<f:submit name="replace" value="${%Apply configuration}"/>
</f:form>
</l:isAdmin>
</l:dialog>

<l:app-bar title="${it.displayName}">
<j:if test="${!empty it.sources}">
<button id="btn-open-apply-configuration" class="jenkins-button"
data-type="dialog-opener"
data-dialog-id="${dialogId}">
<l:icon src="symbol-add"/>
${%Apply configuration}
</button>
</j:if>

<f:form method="post" action="reload" name="reload">
<l:hasAdministerOrManage>
<f:submit icon="symbol-refresh" primary="false" name="reload"
value="${%Reload existing configuration}"/>
</l:hasAdministerOrManage>
</f:form>
<f:form method="post" action="viewExport" name="viewExport">
<l:hasPermission permission="${app.SYSTEM_READ}">
<f:submit primary="false" name="viewExport" value="${%Export configuration}"/>
<button name="viewExport" class="jenkins-button">
${%Export configuration}
</button>
</l:hasPermission>
</f:form>

<j:if test="${!empty it.sources}">
<f:form method="post" action="reload" name="reload">
<l:hasAdministerOrManage>
<button name="reload" tooltip="${%Reload configuration}" class="jenkins-button">
<l:icon src="symbol-refresh"/>
</button>
</l:hasAdministerOrManage>
</f:form>
</j:if>
</l:app-bar>

<p class="jenkins-page-description">
${it.description}
</p>

<j:choose>
<j:when test="${empty it.sources}">
<p>${%Controller has no configuration as code file set.}</p>
<l:notice icon="symbol-logo plugin-configuration-as-code"
title="${%Controller has no configuration as code file set}">
<l:isAdmin>
<button id="btn-open-apply-configuration" class="jenkins-button"
data-type="dialog-opener"
data-dialog-id="${dialogId}">
${%Setup configuration}
</button>
</l:isAdmin>
</l:notice>
</j:when>
<j:otherwise>
${%Configuration loaded from :}
<ul>
<j:forEach var="source" items="${it.sources}">
<li>${source}</li>
</j:forEach>
</ul>

<p>${%Last time applied:} <i:formatDate value="${it.lastTimeLoaded}" type="both" dateStyle="medium" timeStyle="long"/></p>
<f:section title="${%Configurations}">
<p class="jenkins-section__description">
${%Last time applied:}
<i:formatDate value="${it.lastTimeLoaded}" type="both" dateStyle="medium"
timeStyle="long"/>
</p>

<ul class="jenkins-instructions">
<j:forEach var="source" items="${it.sources}">
<li style="font-family: var(--font-family-mono)">${source}</li>
</j:forEach>
</ul>
</f:section>
</j:otherwise>
</j:choose>

<l:isAdmin>
<f:form method="post" action="replace" name="replace">
<h2>${%Replace configuration source with:}</h2>
<f:entry title="${%Path or URL}" field="newSource" class="jenkins-form-item--small" >
<f:textbox checkUrl="checkNewSource" checkDependsOn="newSource"/>
</f:entry>
<f:block>
<f:submit name="replace" value="${%Apply new configuration}"/>
</f:block>
</f:form>
</l:isAdmin>

<l:hasPermission permission="${app.SYSTEM_READ}">
<h2>${%Reference}</h2>
<dt>
<dl><a href="reference">${%Documentation}</a></dl>
<dl><a href="schema">${%JSON schema}</a></dl>
</dt>
<f:section title="${%Reference}">
<dt>
<dl>
<a href="reference">${%Documentation}</a>
</dl>
<dl>
<a href="schema">${%JSON schema}</a>
</dl>
</dt>
</f:section>
</l:hasPermission>
</l:main-panel>
</l:layout>
</l:settings-subpage>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:st="jelly:stapler" xmlns:p="/prism"
xmlns:f="/lib/form">
<l:layout type="one-column" title="${%Exported configuration}">
<l:breadcrumb title="${%Exported configuration}" />
<l:main-panel>
<st:adjunct includes="io.jenkins.plugins.casc.assets.viewExport" />

<l:app-bar title="${%Exported configuration}">
<f:form method="post" action="export" name="export">
<f:submit icon="symbol-download" primary="false" name="export" value="${%Download}"/>
</f:form>
</l:app-bar>
<j:set var="header">
<l:view>
<l:app-bar title="${viewExport.displayName}">
<f:form method="post" action="export" name="export">
<f:submit icon="symbol-download" primary="false" name="export" value="${%Download}"/>
</f:form>
</l:app-bar>
</l:view>
</j:set>

<l:settings-subpage includeBreadcrumb="true" header="${header}" managementLink="${viewExport}" noDefer="true">
<st:adjunct includes="io.jenkins.plugins.casc.assets.viewExport" />

<div class="jenkins-alert jenkins-alert-info">
${%exportWarning}
</div>

<p:prism configuration="${it.prismConfiguration}" />
<pre>
<code class="language-yaml">${export}</code>
<code class="language-yaml">${viewExport.urlName}</code>
</pre>
</l:main-panel>
</l:layout>
</l:settings-subpage>
</j:jelly>
14 changes: 12 additions & 2 deletions plugin/src/test/java/io/jenkins/plugins/casc/ErrorPageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.htmlunit.html.HtmlButton;
import org.htmlunit.html.HtmlElementUtil;
import org.htmlunit.html.HtmlForm;
import org.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -47,7 +49,10 @@ private String reloadConfiguration() throws Exception {
System.setProperty(ConfigurationAsCode.CASC_JENKINS_CONFIG_PROPERTY, cascFile.toString());

HtmlPage htmlPage = webClient.goTo("manage/configuration-as-code/");
HtmlForm reload = htmlPage.getFormByName("reload");
HtmlButton button = (HtmlButton) htmlPage.getElementById("btn-open-apply-configuration");
HtmlElementUtil.click(button);

HtmlForm reload = htmlPage.getFormByName("replace");
HtmlPage submit = r.submit(reload);

return submit.asNormalizedText();
Expand All @@ -67,7 +72,12 @@ void noConfigurator() throws Exception {

@Test
void noImplementationFoundForSymbol() throws Exception {
String content = "jenkins:\n" + " securityRealm:\n" + " unknown:\n" + " username: \"world\"\n";
String content = """
jenkins:
securityRealm:
unknown:
username: "world"
""";

Files.write(cascFile, content.getBytes());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

public enum Action {
VIEW_CONFIGURATION("Export configuration"),
APPLY_NEW_CONFIGURATION("Apply new configuration"),
RELOAD_EXISTING_CONFIGURATION("Reload existing configuration"),
APPLY_NEW_CONFIGURATION("Setup configuration"),
;

String buttonText;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.jenkins.plugins.casc.permissions;

import static io.jenkins.plugins.casc.permissions.Action.APPLY_NEW_CONFIGURATION;
import static io.jenkins.plugins.casc.permissions.Action.RELOAD_EXISTING_CONFIGURATION;
import static io.jenkins.plugins.casc.permissions.Action.VIEW_CONFIGURATION;
import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
Expand Down Expand Up @@ -62,7 +61,6 @@ void checkPermissionsForSystemReader(JenkinsRule j) throws Exception {
ImmutableMap.<Action, Boolean>builder()
.put(VIEW_CONFIGURATION, true)
.put(APPLY_NEW_CONFIGURATION, false)
.put(RELOAD_EXISTING_CONFIGURATION, false)
.build());
}

Expand All @@ -81,11 +79,7 @@ void checkPermissionsForManager(JenkinsRule j) throws Exception {
JenkinsRule.WebClient webClient = j.createWebClient().withThrowExceptionOnFailingStatusCode(false);

assertUserPermissions(
webClient,
MANAGER,
ImmutableMap.<Action, Boolean>builder()
.put(RELOAD_EXISTING_CONFIGURATION, true)
.build());
webClient, MANAGER, ImmutableMap.<Action, Boolean>builder().build());
}

@Test
Expand All @@ -105,7 +99,6 @@ void checkPermissionsForAdmin(JenkinsRule j) throws Exception {
ImmutableMap.<Action, Boolean>builder()
.put(VIEW_CONFIGURATION, true)
.put(APPLY_NEW_CONFIGURATION, true)
.put(RELOAD_EXISTING_CONFIGURATION, true)
.build());
}

Expand Down
5 changes: 3 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
<properties>
<changelist>999999-SNAPSHOT</changelist>
<gitHubRepo>jenkinsci/configuration-as-code-plugin</gitHubRepo>
<jenkins.baseline>2.504</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.3</jenkins.version>
<jenkins.baseline>2.528</jenkins.baseline>
<!-- TODO change back to baseline once next bom version is out -->
<jenkins.version>2.541.1</jenkins.version>
<tagNameFormat>configuration-as-code-@{project.version}</tagNameFormat>
<plugin-bom.version>5983.v443959746f1f</plugin-bom.version>
<spotless.check.skip>false</spotless.check.skip>
Expand Down
Loading
Loading