Skip to content

Commit e41bc70

Browse files
committed
Move user settings to YAML-based configuration
1 parent 4de21ea commit e41bc70

File tree

8 files changed

+319
-4
lines changed

8 files changed

+319
-4
lines changed

imagetool/pom.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<parent>
1414
<artifactId>imagetool-parent</artifactId>
1515
<groupId>com.oracle.weblogic.lifecycle.imagetool</groupId>
16-
<version>1.11.3-SNAPSHOT</version>
16+
<version>2.0.0-SNAPSHOT</version>
1717
<relativePath>../pom.xml</relativePath>
1818
</parent>
1919

@@ -51,6 +51,10 @@
5151
<groupId>uk.org.webcompere</groupId>
5252
<artifactId>system-stubs-jupiter</artifactId>
5353
</dependency>
54+
<dependency>
55+
<groupId>org.yaml</groupId>
56+
<artifactId>snakeyaml</artifactId>
57+
</dependency>
5458
</dependencies>
5559

5660
<build>
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// Copyright (c) 2022, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package com.oracle.weblogic.imagetool.settings;
5+
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
import java.util.Map;
12+
13+
import com.oracle.weblogic.imagetool.logging.LoggingFacade;
14+
import com.oracle.weblogic.imagetool.logging.LoggingFactory;
15+
import com.oracle.weblogic.imagetool.util.Utils;
16+
import org.yaml.snakeyaml.DumperOptions;
17+
import org.yaml.snakeyaml.Yaml;
18+
import org.yaml.snakeyaml.introspector.PropertyUtils;
19+
import org.yaml.snakeyaml.nodes.Tag;
20+
import org.yaml.snakeyaml.representer.Representer;
21+
22+
public class UserSettings {
23+
private static final LoggingFacade logger = LoggingFactory.getLogger(UserSettings.class);
24+
25+
/**
26+
* Parent directory for the build context directory.
27+
* A temporary folder created under "Build Directory" with the prefix "wlsimgbuilder_tempXXXXXXX" will be created
28+
* to hold the image build context (files, and Dockerfile).
29+
*/
30+
private final String imageBuildDirectory;
31+
32+
/**
33+
* Patch download directory.
34+
* The directory for storing and using downloaded patches.
35+
*/
36+
private final String patchDirectory;
37+
38+
/**
39+
* Installer download directory.
40+
* The directory for storing and using downloaded Java and middleware installers.
41+
*/
42+
private final String installerDirectory;
43+
44+
/**
45+
* Container image build tool.
46+
* Allow the user to specify the executable that will be used to build the container image. For example,
47+
* "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker".
48+
*/
49+
private final String buildEngine;
50+
51+
/**
52+
* Container image runtime tool.
53+
* Allow the user to specify the executable that will be used to run and/or interrogate images. For example,
54+
* "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker".
55+
*/
56+
private final String containerEngine;
57+
58+
/**
59+
* REST calls to ARU should be retried up to this number of times.
60+
*/
61+
private final Integer aruRetryMax;
62+
63+
/**
64+
* The time between each ARU REST call in milliseconds.
65+
*/
66+
private final Integer aruRetryInterval;
67+
68+
/**
69+
* Default construct with all default values for settings.
70+
*/
71+
public UserSettings() {
72+
patchDirectory = null;
73+
installerDirectory = null;
74+
imageBuildDirectory = null;
75+
buildEngine = null;
76+
containerEngine = null;
77+
78+
aruRetryMax = null;
79+
aruRetryInterval = null;
80+
}
81+
82+
/**
83+
* Extract the Map of settings (from a YAML file), into a Java Bean, UserSettings.
84+
* @param settings A map of key-value pairs read in from the YAML user settings file.
85+
*/
86+
public UserSettings(Map<String, Object> settings) {
87+
// While yaml.loadAs() will do about the same thing, I opted to use Map because Map is more forgiving.
88+
// Bad fields or extra data fields not in this version of ImageTool will cause yaml.loadAs to completely fail.
89+
patchDirectory = getValue("patchDirectory", String.class, settings);
90+
installerDirectory = getValue("installerDirectory", String.class, settings);
91+
imageBuildDirectory = getValue("imageBuildDirectory", String.class, settings);
92+
buildEngine = getValue("buildEngine", String.class, settings);
93+
containerEngine = getValue("containerEngine", String.class, settings);
94+
95+
aruRetryMax = getValue("aruRetryMax", Integer.class, settings);
96+
aruRetryInterval = getValue("aruRetryInterval", Integer.class, settings);
97+
}
98+
99+
/**
100+
* The file system path to the directory where the settings file should be.
101+
* @return The path to ~/.imagetool
102+
*/
103+
public static Path getSettingsDirectory() {
104+
return Paths.get(System.getProperty("user.home"), ".imagetool");
105+
}
106+
107+
/**
108+
* Loads the settings.yaml file from ~/.imagetool/settings.yaml and returns the values as UserSettings.
109+
* @return The user settings parsed from ~/.imagetool/settings.yaml
110+
*/
111+
public static UserSettings instance() {
112+
Path settingsFile = getSettingsDirectory().resolve("settings.yaml");
113+
try (InputStream input = Files.newInputStream(settingsFile)) {
114+
return load(input);
115+
} catch (IOException ioe) {
116+
logger.fine("Unable to open saved settings: {0}", ioe.getMessage(), ioe);
117+
return new UserSettings();
118+
}
119+
}
120+
121+
/**
122+
* Utility method to convert the InputStream with YAML into UserSettings.
123+
* @param settings An InputStream with the raw YAML text.
124+
* @return The UserSettings containing the parsed values from the InputStream.
125+
*/
126+
public static UserSettings load(InputStream settings) {
127+
Yaml yaml = new Yaml();
128+
Map<String, Object> map = yaml.load(settings);
129+
return new UserSettings(map);
130+
}
131+
132+
private <T> T getValue(String settingName, Class<T> type, Map<String, Object> settings) {
133+
Object value = settings.get(settingName);
134+
if (value == null) {
135+
return null;
136+
}
137+
138+
if (type.isInstance(value)) {
139+
return type.cast(value);
140+
} else {
141+
logger.severe("Setting for {0} could not be loaded. Expected {1}, but found {2}. Invalid value: {3}",
142+
settingName, type, value.getClass(), value.toString());
143+
return null;
144+
}
145+
}
146+
147+
/**
148+
* Parent directory for the build context directory.
149+
* A temporary folder created under "Build Directory" with the prefix "wlsimgbuilder_tempXXXXXXX" will be created
150+
* to hold the image build context (files, and Dockerfile).
151+
*/
152+
public String getImageBuildDirectory() {
153+
if (Utils.isEmptyString(imageBuildDirectory)) {
154+
return ".";
155+
}
156+
return imageBuildDirectory;
157+
}
158+
159+
/**
160+
* Patch download directory.
161+
* The directory for storing and using downloaded patches.
162+
*/
163+
public String getPatchDirectory() {
164+
if (Utils.isEmptyString(patchDirectory)) {
165+
return getSettingsDirectory().resolve("patches").toString();
166+
}
167+
return patchDirectory;
168+
}
169+
170+
/**
171+
* Installer download directory.
172+
* The directory for storing and using downloaded Java and middleware installers.
173+
*/
174+
public String installerDirectory() {
175+
if (Utils.isEmptyString(installerDirectory)) {
176+
return getSettingsDirectory().resolve("installers").toString();
177+
}
178+
return installerDirectory;
179+
}
180+
181+
/**
182+
* Container image build tool.
183+
* Allow the user to specify the executable that will be used to build the container image. For example,
184+
* "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker".
185+
*/
186+
public String getBuildEngine() {
187+
if (Utils.isEmptyString(buildEngine)) {
188+
return "docker";
189+
}
190+
return buildEngine;
191+
}
192+
193+
/**
194+
* Container image runtime tool.
195+
* Allow the user to specify the executable that will be used to run and/or interrogate images. For example,
196+
* "/usr/local/bin/docker" or just "docker" if "docker" is on the user's path. For example, "podman" or "docker".
197+
*/
198+
public String getContainerEngine() {
199+
if (Utils.isEmptyString(containerEngine)) {
200+
return getBuildEngine();
201+
}
202+
return containerEngine;
203+
}
204+
205+
/**
206+
* REST calls to ARU should be retried up to this number of times.
207+
*/
208+
public int getAruRetryMax() {
209+
if (aruRetryMax == null) {
210+
return 10;
211+
}
212+
return aruRetryMax;
213+
}
214+
215+
/**
216+
* The time between each ARU REST call in milliseconds.
217+
*/
218+
public int getAruRetryInterval() {
219+
if (aruRetryInterval == null) {
220+
return 500;
221+
}
222+
return aruRetryInterval;
223+
}
224+
225+
/**
226+
* UserSettings as a YAML string.
227+
* @return UserSettings as a YAML string.
228+
*/
229+
public String toYamlString() {
230+
DumperOptions options = new DumperOptions();
231+
options.setIndent(2);
232+
options.setPrettyFlow(true);
233+
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
234+
235+
PropertyUtils propertyUtils = new PropertyUtils();
236+
propertyUtils.setAllowReadOnlyProperties(true);
237+
Representer representer = new Representer();
238+
representer.addClassTag(UserSettings.class, Tag.MAP);
239+
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
240+
representer.setPropertyUtils(propertyUtils);
241+
242+
Yaml yaml = new Yaml(representer);
243+
return yaml.dump(this);
244+
}
245+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2022, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
package com.oracle.weblogic.imagetool.settings;
5+
6+
import java.io.InputStream;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
12+
class UserSettingsTest {
13+
14+
@Test
15+
void testSimpleSettingsFile() {
16+
InputStream inputStream = this.getClass()
17+
.getClassLoader()
18+
.getResourceAsStream("settings/basic_settings.yaml");
19+
UserSettings settings = UserSettings.load(inputStream);
20+
assertEquals("/home/user/patches", settings.getPatchDirectory());
21+
assertEquals("./builds", settings.getImageBuildDirectory());
22+
assertEquals("docker", settings.getBuildEngine());
23+
assertEquals("docker", settings.getContainerEngine());
24+
assertEquals(10, settings.getAruRetryMax());
25+
assertEquals(200, settings.getAruRetryInterval());
26+
}
27+
28+
@Test
29+
void testInvalidSettings() {
30+
InputStream inputStream = this.getClass()
31+
.getClassLoader()
32+
.getResourceAsStream("settings/invalid_settings.yaml");
33+
UserSettings settings = UserSettings.load(inputStream);
34+
assertEquals("/home/user/patches", settings.getPatchDirectory());
35+
assertEquals(".", settings.getImageBuildDirectory());
36+
}
37+
38+
//@Test
39+
void testOutput() {
40+
//TODO: re-enable this test
41+
String expected = "aruRetryInterval: 200\n"
42+
+ "imageBuildDirectory: ./builds\n"
43+
+ "patchDirectory: /home/user/patches\n";
44+
45+
InputStream inputStream = this.getClass()
46+
.getClassLoader()
47+
.getResourceAsStream("settings/basic_settings.yaml");
48+
UserSettings settings = UserSettings.load(inputStream);
49+
assertEquals(expected, settings.toYamlString());
50+
}
51+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
aruRetryInterval: 200
2+
imageBuildDirectory: ./builds
3+
patchDirectory: /home/user/patches
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
patchDirectory: /home/user/patches
2+
imageBuildDirectory: 1
3+
someField: someValue

installer/pom.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<parent>
1414
<artifactId>imagetool-parent</artifactId>
1515
<groupId>com.oracle.weblogic.lifecycle.imagetool</groupId>
16-
<version>1.11.3-SNAPSHOT</version>
16+
<version>2.0.0-SNAPSHOT</version>
1717
<relativePath>../pom.xml</relativePath>
1818
</parent>
1919

@@ -39,6 +39,10 @@
3939
<groupId>com.github.spullara.mustache.java</groupId>
4040
<artifactId>compiler</artifactId>
4141
</dependency>
42+
<dependency>
43+
<groupId>org.yaml</groupId>
44+
<artifactId>snakeyaml</artifactId>
45+
</dependency>
4246
</dependencies>
4347

4448
<build>

pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<modelVersion>4.0.0</modelVersion>
99
<groupId>com.oracle.weblogic.lifecycle.imagetool</groupId>
1010
<artifactId>imagetool-parent</artifactId>
11-
<version>1.11.3-SNAPSHOT</version>
11+
<version>2.0.0-SNAPSHOT</version>
1212
<packaging>pom</packaging>
1313

1414
<name>WebLogic Image Tool</name>
@@ -92,6 +92,11 @@
9292
<artifactId>annotations</artifactId>
9393
<version>18.0.0</version>
9494
</dependency>
95+
<dependency>
96+
<groupId>org.yaml</groupId>
97+
<artifactId>snakeyaml</artifactId>
98+
<version>1.29</version>
99+
</dependency>
95100
</dependencies>
96101
</dependencyManagement>
97102

tests/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<parent>
1313
<artifactId>imagetool-parent</artifactId>
1414
<groupId>com.oracle.weblogic.lifecycle.imagetool</groupId>
15-
<version>1.11.3-SNAPSHOT</version>
15+
<version>2.0.0-SNAPSHOT</version>
1616
<relativePath>../pom.xml</relativePath>
1717
</parent>
1818

0 commit comments

Comments
 (0)