Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): Added json schema generator for providers #7

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
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: 2 additions & 0 deletions halyard-cli/halyard-cli.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ dependencies {
force = true
}
implementation 'org.nibor.autolink:autolink:0.10.0'
implementation 'org.reflections:reflections:0.9.11'
implementation 'net.minidev:json-smart:1.1.1'

implementation project(':halyard-config')
implementation project(':halyard-core')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.netflix.spinnaker.halyard.cli.command.v1;

import java.lang.reflect.Field;
import java.util.HashMap;

import com.beust.jcommander.Parameter;
import com.netflix.spinnaker.halyard.config.model.v1.node.Providers;

import lombok.Data;
import net.minidev.json.JSONObject;
@Data
public class EnrichMetadata {
public JSONObject providersFields = Providers.providersMetadata();

public void addProviderCommandFields(String providerName, Field[] fields) {
JSONObject providerFields = (JSONObject) ((JSONObject) providersFields.get("providers")).get(providerName);
Copy link
Author

Choose a reason for hiding this comment

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

why the double cast? (JSONObject) ((JSONObject)....

if (providersFields != null && providerFields != null) {
for (Field f : fields) {
if (f.getAnnotation(Parameter.class) != null && providerFields.get(f.getName()) != null) {
if (f.getAnnotation(Parameter.class).required() == true) {
((HashMap<String, Object>) providerFields.get(f.getName())).put("required", true);
}
if (f.getAnnotation(Parameter.class).description() != "") {
((HashMap<String, Object>) providerFields.get(f.getName()))
.put("description", f.getAnnotation(Parameter.class).description());
}
}
}
}
}
public void addAccountCommandFields(String providerName, Field[] fields) {
Copy link
Author

Choose a reason for hiding this comment

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

add new line above

JSONObject accountfields = (JSONObject) ((JSONObject) ((JSONObject) providersFields.get("providers")).get(providerName))
.get("accountFields");
if (providersFields != null && accountfields != null) {
for (Field f : fields) {
if (f.getAnnotation(Parameter.class) != null && accountfields.get(f.getName()) != null) {
if (f.getAnnotation(Parameter.class).required() == true) {
((HashMap<String, Object>) accountfields.get(f.getName())).put("required", true);
}
if (f.getAnnotation(Parameter.class).description() != "") {
((HashMap<String, Object>) accountfields.get(f.getName()))
.put("description", f.getAnnotation(Parameter.class).description());
}
}
}
}
}
public void addBakeryCommandFields(String providerName, Field[] fields) {
Copy link
Author

Choose a reason for hiding this comment

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

add new line above

JSONObject bakeryDefaultsFields = (JSONObject) ((JSONObject) ((JSONObject) providersFields.get("providers"))
.get(providerName)).get("bakeryDefaultsFields");
if (providersFields != null && bakeryDefaultsFields != null) {
Copy link
Author

Choose a reason for hiding this comment

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

might want to use Objects.equals instead of !=, better null safety

for (Field f : fields) {
if (f.getAnnotation(Parameter.class) != null && bakeryDefaultsFields.get(f.getName()) != null) {
Copy link
Author

Choose a reason for hiding this comment

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

might want to use Objects.equals instead of !=, better null safety

if (f.getAnnotation(Parameter.class).required() == true) {
((HashMap<String, Object>) bakeryDefaultsFields.get(f.getName())).put("required", true);
}
if (f.getAnnotation(Parameter.class).description() != "") {
((HashMap<String, Object>) bakeryDefaultsFields.get(f.getName()))
.put("description", f.getAnnotation(Parameter.class).description());
}
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.netflix.spinnaker.halyard.cli.command.v1;

import com.netflix.spinnaker.halyard.cli.command.v1.config.providers.AbstractProviderCommand;
import com.netflix.spinnaker.halyard.cli.command.v1.config.providers.account.AbstractAddAccountCommand;
import com.netflix.spinnaker.halyard.cli.command.v1.config.providers.bakery.AbstractEditBakeryDefaultsCommand;
import com.netflix.spinnaker.halyard.config.model.v1.node.Provider.ProviderType;
import com.netflix.spinnaker.halyard.config.model.v1.node.Providers;
import java.lang.reflect.Field;
import java.util.Arrays;
import lombok.Data;
import net.minidev.json.JSONObject;

@Data
public class GenerateMetadata {
public static JSONObject generateMetadata() {
EnrichMetadata em = new EnrichMetadata();
for (ProviderType provider : ProviderType.values()) {
try {
Class<?> providerCommandClass = translateProviderCommandType(provider.getName());
Field[] providerCommandFields = getFields(providerCommandClass);
em.addProviderCommandFields(provider.getName(), providerCommandFields);

} catch (IllegalArgumentException e) {
// ignoring because it doesn't matter
}
}
for (ProviderType provider : ProviderType.values()) {
try {
Class<?> addBakeryCommandClass = translateEditBakeryCommandType(provider.getName());
Field[] addBakeryCommandFields = getFields(addBakeryCommandClass);
em.addBakeryCommandFields(provider.getName(), addBakeryCommandFields);
} catch (IllegalArgumentException e) {
// ignoring because it doesn't matter
}
}
for (ProviderType provider : ProviderType.values()) {
try {
Class<?> addCommandClass = translateAddAccountCommandType(provider.getName());
Field[] addCommandFields = getFields(addCommandClass);
em.addAccountCommandFields(provider.getName(), addCommandFields);
} catch (IllegalArgumentException e) {
// ignoring because it doesn't matter
}
}
return em.getProvidersFields();
}

public static Class<? extends AbstractAddAccountCommand> translateAddAccountCommandType(
String providerName) {
Class<?> providerClass = Providers.translateProviderType(providerName);

String addAccountCommandClass =
providerClass
.getName()
.replaceAll(
"com.netflix.spinnaker.halyard.config.model.v1.providers",
"com.netflix.spinnaker.halyard.cli.command.v1.config.providers")
.replaceAll("Provider", "AddAccountCommand");
try {
return (Class<? extends AbstractAddAccountCommand>) Class.forName(addAccountCommandClass);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(
"No account for class \"" + addAccountCommandClass + "\" found", e);
}
}

public static Class<? extends AbstractEditBakeryDefaultsCommand> translateEditBakeryCommandType(
String providerName) {
Class<?> providerClass = Providers.translateProviderType(providerName);

String editBakeryDefaultsCommand =
providerClass
.getName()
.replaceAll(
"com.netflix.spinnaker.halyard.config.model.v1.providers",
"com.netflix.spinnaker.halyard.cli.command.v1.config.providers")
.replaceAll("Provider", "EditBakeryDefaultsCommand");
try {
return (Class<? extends AbstractEditBakeryDefaultsCommand>)
Class.forName(editBakeryDefaultsCommand);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(
"No account for class \"" + editBakeryDefaultsCommand + "\" found", e);
}
}

public static Class<? extends AbstractProviderCommand> translateProviderCommandType(
String providerName) {
Class<?> providerClass = Providers.translateProviderType(providerName);

String editBakeryDefaultsCommand =
providerClass
.getName()
.replaceAll(
"com.netflix.spinnaker.halyard.config.model.v1.providers",
"com.netflix.spinnaker.halyard.cli.command.v1.config.providers")
.replaceAll("Provider", "EditProviderCommand");
try {
return (Class<? extends AbstractEditBakeryDefaultsCommand>)
Class.forName(editBakeryDefaultsCommand);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(
"No account for class \"" + editBakeryDefaultsCommand + "\" found", e);
}
}

public static Field[] getFields(Class<?> c) {
Field[] extendedFields = c.getSuperclass().getDeclaredFields();
Field[] fields = c.getDeclaredFields();
Field[] allFields = new Field[extendedFields.length + fields.length];
Arrays.setAll(
allFields,
i -> (i < extendedFields.length ? extendedFields[i] : fields[i - extendedFields.length]));
return allFields;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,9 @@ protected void executeThis() {
if (docs) {
System.out.println(generateDocs());
}

if (version) {
System.out.println(getVersion());
}

if (printBashCompletion) {
System.out.println(commandCompletor());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,36 @@
import com.netflix.spinnaker.halyard.cli.ui.v1.AnsiStoryBuilder;
import com.netflix.spinnaker.halyard.cli.ui.v1.AnsiStyle;
import com.netflix.spinnaker.halyard.cli.ui.v1.AnsiUi;
import com.netflix.spinnaker.halyard.config.model.v1.node.Account;
import com.netflix.spinnaker.halyard.config.model.v1.node.BakeryDefaults;
import com.netflix.spinnaker.halyard.config.model.v1.node.Providers;
import com.netflix.spinnaker.halyard.core.job.v1.JobExecutor;
import com.netflix.spinnaker.halyard.core.job.v1.JobExecutorLocal;
import com.netflix.spinnaker.halyard.core.resource.v1.StringReplaceJarResource;
import java.io.Console;
import java.lang.reflect.Field;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.nibor.autolink.*;
import net.minidev.json.JSONObject;
import org.nibor.autolink.LinkExtractor;
import org.nibor.autolink.LinkSpan;
import org.nibor.autolink.LinkType;
import org.nibor.autolink.Span;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import retrofit.RetrofitError;

@Parameters(separators = "=")
Expand Down Expand Up @@ -629,7 +641,8 @@ public void configureSubcommands() {

commander.addCommand(subCommand.getCommandName(), subCommand);

// We need to provide the subcommand with its own commander before recursively populating its
// We need to provide the subcommand with its own commander before recursively
// populating its
// subcommands, since
// they need to be registered with this subcommander we retrieve here.
JCommander subCommander = commander.getCommands().get(subCommand.getCommandName());
Expand Down
3 changes: 2 additions & 1 deletion halyard-config/halyard-config.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ dependencies {
implementation 'io.fabric8:kubernetes-client'
implementation 'com.squareup.retrofit:retrofit'
implementation 'com.jcraft:jsch'

implementation 'net.minidev:json-smart:1.1.1'
implementation('com.beust:jcommander:1.71')
// TODO: add clouddriverDCOS once that's merged
implementation project(':halyard-core')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@

package com.netflix.spinnaker.halyard.config.model.v1.node;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import lombok.Data;
import net.minidev.json.JSONObject;

@Data
public class ProviderDescriptor {
public JSONObject allProviderFields = new JSONObject();
public JSONObject providersFields = new JSONObject();

public JSONObject initialProviderFields() {
// Fields for most providers: enabled, account, primaryAccount
JSONObject providerFields = new JSONObject();
Field[] initialFields = Provider.class.getDeclaredFields();
for (Field field : initialFields) {
providerFields.put(field.getName(), fieldType(field));
}
return providerFields;
}

public JSONObject initialAccountFields() {
// Fields for most accounts: name, version ect
JSONObject accountFields = new JSONObject();
Field[] initialFields = Account.class.getDeclaredFields();
for (Field field : initialFields) {
accountFields.put(field.getName(), fieldType(field));
}
return accountFields;
}

public JSONObject initialBakeryFields() {
JSONObject bakeryFields = new JSONObject();
Field[] initialFields = BakeryDefaults.class.getDeclaredFields();
for (Field field : initialFields) {
bakeryFields.put(field.getName(), fieldType(field));
}
return bakeryFields;
}

public void addProviderField(String providerName, Field[] extraFields) {
// some providers e.g aws, appengine have extra fields
JSONObject addFields = initialProviderFields();
for (Field field : extraFields) {
addFields.put(field.getName(), fieldType(field));
}
if (providerName != null) {
providersFields.put(providerName, addFields);
}
}

public void addAccountField(String providerName, Field[] fields) {
JSONObject accountFields = initialAccountFields();
for (Field field : fields) {
accountFields.put(field.getName(), fieldType(field));
}
if (providersFields != null && providersFields.get(providerName) != null) {
Copy link
Author

Choose a reason for hiding this comment

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

might want to use Objects.equals instead of !=, better null safety

((HashMap<String, Object>) providersFields.get(providerName)).put("accountFields", accountFields);

}
}

public void addBakeryField(String providerName, Field[] fields) {
JSONObject bakeryFields = initialBakeryFields();
for (Field field : fields) {
bakeryFields.put(field.getName(), fieldType(field));
}
if (providersFields != null && providersFields.get(providerName) != null) {
Copy link
Author

Choose a reason for hiding this comment

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

might want to use Objects.equals instead of !=, better null safety

((HashMap<String, Object>) providersFields.get(providerName)).put("useBakeryDefaults", "boolean");
((HashMap<String, Object>) providersFields.get(providerName)).put("bakeryDefaultsFields", bakeryFields);
}
}

public JSONObject allProviderFields() {
allProviderFields.put("providers", providersFields);
return allProviderFields;
}

public JSONObject fieldType(Field field) {
JSONObject typeWrapper = new JSONObject();
if (field.getAnnotation(LocalFile.class) != null) {
Copy link
Author

Choose a reason for hiding this comment

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

might want to use Objects.equals instead of !=, better null safety

typeWrapper.put("type", "upload");
} else if (field.getAnnotation(Secret.class) != null) {
Copy link
Author

Choose a reason for hiding this comment

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

might want to use Objects.equals instead of !=, better null safety

typeWrapper.put("type", "password");
} else if (field.getType().isEnum()) {
Object[] objects = field.getType().getEnumConstants();
List<Object> enumConstantsList = new ArrayList<>();
for (Object obj : objects) {
enumConstantsList.add(obj);
}
typeWrapper.put("enum", enumConstantsList);
typeWrapper.put("type", "string");

} else if (field.getType() == (List.class) && !field.getName().equals("accounts")) {
// assume a list is a list of strings
typeWrapper.put("type", "stringlist");
} else {
typeWrapper.put("type", field.getType().getSimpleName().toLowerCase());
}
return typeWrapper;
}

}
Loading