Skip to content

Commit e842e3b

Browse files
[SPARK-49527] Add ConfOptionDocGenerator to generate Spark Operator Config Property Doc
### What changes were proposed in this pull request? This PR adds a module `docs-utils` to automatically generate config properties doc page from source. ### Why are the changes needed? This helps to keep config property docs up-to-date for properties by adding it as a gradle task. ### Does this PR introduce _any_ user-facing change? No (doc only, not released) ### How was this patch tested? Pass the CIs ### Was this patch authored or co-authored using generative AI tooling? No Closes #118 from jiangzho/doc_utils. Lead-authored-by: zhou-jiang <[email protected]> Co-authored-by: Dongjoon Hyun <[email protected]> Signed-off-by: Dongjoon Hyun <[email protected]>
1 parent 7ffee1e commit e842e3b

File tree

5 files changed

+200
-2
lines changed

5 files changed

+200
-2
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
ext {
21+
javaMainClass = "org.apache.spark.k8s.operator.utils.ConfOptionDocGenerator"
22+
docsPath = System.getProperty("user.dir") + "/docs"
23+
}
24+
25+
dependencies {
26+
implementation project(":spark-operator")
27+
implementation(libs.log4j.core)
28+
implementation(libs.log4j.slf4j.impl)
29+
compileOnly(libs.lombok)
30+
annotationProcessor(libs.lombok)
31+
}
32+
33+
test {
34+
useJUnitPlatform()
35+
}
36+
37+
tasks.register('generateConfPropsDoc', Exec) {
38+
description = "Generate config properties doc for operator"
39+
commandLine "java", "-classpath", sourceSets.main.runtimeClasspath.getAsPath(), javaMainClass, docsPath
40+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.spark.k8s.operator.utils;
21+
22+
import java.io.File;
23+
import java.io.IOException;
24+
import java.io.PrintWriter;
25+
import java.lang.reflect.Field;
26+
import java.util.List;
27+
28+
import lombok.extern.slf4j.Slf4j;
29+
30+
import org.apache.spark.k8s.operator.config.ConfigOption;
31+
import org.apache.spark.k8s.operator.config.SparkOperatorConf;
32+
33+
@Slf4j
34+
public class ConfOptionDocGenerator {
35+
public static final String CONF_FILE_NAME = "config_properties.md";
36+
public static final String DEFAULT_DOCS_PATH = "docs";
37+
public static final String GENERATED_FILE_HEADER =
38+
"This doc is automatically generated by gradle task, manual updates would be overridden.";
39+
40+
public void generate(String docsPath) throws IOException, IllegalAccessException {
41+
Field[] fields = SparkOperatorConf.class.getDeclaredFields();
42+
File docsDir = new File(docsPath);
43+
if (!docsDir.exists() && docsDir.mkdir()) {
44+
log.info("Creating docs directory at {}", docsPath);
45+
}
46+
File generated = new File(docsPath, CONF_FILE_NAME);
47+
if (generated.createNewFile()) {
48+
log.info("Creating props at {}/{}", docsPath, CONF_FILE_NAME);
49+
}
50+
PrintWriter printWriter = new PrintWriter(generated, "UTF-8");
51+
printWriter.println(String.format("[//]: # (%s)", GENERATED_FILE_HEADER));
52+
printWriter.println("# Spark Operator Config Properties");
53+
DocTable table =
54+
DocTable.builder()
55+
.headers(List.of("Key", "Type", "Default Value", "Allow Hot Reloading", "Description"))
56+
.columns(5)
57+
.build();
58+
for (Field f : fields) {
59+
if (ConfigOption.class.isAssignableFrom(f.getType())) {
60+
ConfigOption<?> conf = (ConfigOption<?>) f.get(this);
61+
table.addRow(
62+
List.of(
63+
conf.getKey(),
64+
conf.getTypeParameterClass().getSimpleName(),
65+
conf.getDefaultValue().toString(),
66+
String.valueOf(conf.isEnableDynamicOverride()),
67+
conf.getDescription()));
68+
}
69+
}
70+
table.flush(printWriter);
71+
printWriter.close();
72+
}
73+
74+
public static void main(String[] args) throws IOException, IllegalAccessException {
75+
ConfOptionDocGenerator generator = new ConfOptionDocGenerator();
76+
String docsPath = DEFAULT_DOCS_PATH;
77+
if (args.length > 0) {
78+
docsPath = args[0];
79+
}
80+
try {
81+
generator.generate(docsPath);
82+
} catch (IOException | IllegalAccessException e) {
83+
log.error("Failed to generate docs for config props.", e);
84+
throw e;
85+
}
86+
}
87+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.spark.k8s.operator.utils;
21+
22+
import java.io.PrintWriter;
23+
import java.util.ArrayList;
24+
import java.util.Collections;
25+
import java.util.List;
26+
27+
import lombok.Builder;
28+
import lombok.Data;
29+
import lombok.RequiredArgsConstructor;
30+
31+
@Data
32+
@RequiredArgsConstructor
33+
@Builder
34+
public class DocTable {
35+
// Separator among cells
36+
public static final String ROW_SEPARATOR = " | ";
37+
// Separator for header line
38+
public static final String HEADER_SEPARATOR = "---";
39+
private final List<String> headers;
40+
private final int columns;
41+
@Builder.Default private final List<List<String>> rows = new ArrayList<>();
42+
43+
public void addRow(List<String> row) {
44+
rows.add(row);
45+
}
46+
47+
public void flush(PrintWriter writer) {
48+
writer.println(joinRow(headers));
49+
writer.println(joinRow(Collections.nCopies(columns, HEADER_SEPARATOR)));
50+
for (List<String> row : rows) {
51+
writer.println(joinRow(row));
52+
}
53+
writer.println();
54+
writer.flush();
55+
}
56+
57+
private String joinRow(List<String> elements) {
58+
StringBuilder stringBuilder = new StringBuilder(ROW_SEPARATOR);
59+
for (String element : elements) {
60+
stringBuilder.append(element);
61+
stringBuilder.append(ROW_SEPARATOR);
62+
}
63+
if (elements.size() < columns) {
64+
// Append empty cells to end if needed
65+
stringBuilder.append(ROW_SEPARATOR.repeat(columns - elements.size()));
66+
}
67+
return stringBuilder.toString();
68+
}
69+
}

settings.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ rootProject.name = 'apache-spark-kubernetes-operator'
2020
include 'spark-operator-api'
2121
include 'spark-submission-worker'
2222
include 'spark-operator'
23+
include "build-tools-docs-utils"
24+
project(':build-tools-docs-utils').projectDir = file('build-tools/docs-utils')

spark-operator/src/main/java/org/apache/spark/k8s/operator/config/ConfigOption.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public class ConfigOption<T> {
4242
@Getter @Builder.Default private final boolean enableDynamicOverride = true;
4343
@Getter private String key;
4444
@Getter private String description;
45-
private T defaultValue;
46-
private Class<T> typeParameterClass;
45+
@Getter private T defaultValue;
46+
@Getter private Class<T> typeParameterClass;
4747

4848
public T getValue() {
4949
T resolvedValue = resolveValue();

0 commit comments

Comments
 (0)