Skip to content
Draft
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
47 changes: 47 additions & 0 deletions fractal.sdk/src/main/java/com/yanchware/fractal/sdk/Automaton.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import com.yanchware.fractal.sdk.configuration.EnvVarSdkConfiguration;
import com.yanchware.fractal.sdk.configuration.SdkConfiguration;
import com.yanchware.fractal.sdk.configuration.instantiation.InstantiationConfiguration;
import com.yanchware.fractal.sdk.domain.accounts.AccountAggregate;
import com.yanchware.fractal.sdk.domain.accounts.AccountsFactory;
import com.yanchware.fractal.sdk.domain.accounts.OrganizationAggregate;
import com.yanchware.fractal.sdk.domain.accounts.OrganizationFactory;
import com.yanchware.fractal.sdk.domain.blueprint.BlueprintFactory;
import com.yanchware.fractal.sdk.domain.environment.EnvironmentAggregate;
import com.yanchware.fractal.sdk.domain.environment.EnvironmentIdValue;
Expand Down Expand Up @@ -40,13 +44,17 @@ public class Automaton {
private static BlueprintFactory blueprintFactory;
private static LiveSystemsFactory liveSystemFactory;
private static EnvironmentsFactory environmentsFactory;
private static AccountsFactory accountsFactory;
private static OrganizationFactory organizationFactory;
private static RetryRegistry serviceRetryRegistry;

private Automaton(HttpClient httpClient, SdkConfiguration sdkConfiguration) {
Automaton.serviceRetryRegistry = getDefaultRetryRegistry();
Automaton.blueprintFactory = new BlueprintFactory(httpClient, sdkConfiguration, Automaton.serviceRetryRegistry);
Automaton.liveSystemFactory = new LiveSystemsFactory(httpClient, sdkConfiguration, Automaton.serviceRetryRegistry);
Automaton.environmentsFactory = new EnvironmentsFactory(httpClient, sdkConfiguration, Automaton.serviceRetryRegistry);
Automaton.accountsFactory = new AccountsFactory(httpClient, sdkConfiguration, Automaton.serviceRetryRegistry);
Automaton.organizationFactory = new OrganizationFactory(httpClient, sdkConfiguration, Automaton.serviceRetryRegistry);
}

/**
Expand Down Expand Up @@ -81,6 +89,24 @@ public LiveSystemsFactory.LiveSystemBuilder getLiveSystemBuilder() {
}

/**
* Get builder for Account Aggregate
*
* @return
*/
public AccountsFactory.AccountBuilder getAccountBuilder() {
return accountsFactory.builder();
}

/**
* Get builder for Organization Aggregate
*
* @return
*/
public OrganizationFactory.OrganizationBuilder getOrganizationBuilder() {
return organizationFactory.builder();
}

/**
* Instantiates the given environment.
*
* @param environment the environment to be instantiated
Expand All @@ -91,6 +117,27 @@ public void instantiate(EnvironmentAggregate environment) throws InstantiatorExc
}

/**
* Instantiates the given account.
*
* @param accounts the account to be instantiated
* @throws InstantiatorException if an error occurs
*/
public void instantiate(AccountAggregate accounts) throws InstantiatorException {
accounts.createOrUpdate();
}

/**
* Instantiates the given organization aggregate by reconciling organizational Resource Groups.
*
* @param organization the organization to be instantiated
* @throws InstantiatorException if an error occurs
*/
public void instantiate(OrganizationAggregate organization) throws InstantiatorException {
organization.createOrUpdate();
}


/**
* Instantiates the given list of live systems.
*
* @param liveSystems the list of live systems to be instantiated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public class Constants {
public static final String LIVESYSTEM_ENDPOINT_KEY = "LIVESYSTEM_ENDPOINT";
public static final String BLUEPRINT_ENDPOINT_KEY = "BLUEPRINT_ENDPOINT";
public static final String ENVIRONMENTS_ENDPOINT_KEY = "ENVIRONMENTS_ENDPOINT";
public static final String ACCOUNTS_ENDPOINT_KEY = "ACCOUNTS_ENDPOINT";
public static final String GIT_COMMIT_ID_KEY = "commitId";

public static final String X_CLIENT_ID_HEADER = "X-ClientID";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public class EnvVarSdkConfiguration implements SdkConfiguration {
*/
public final URI DEFAULT_ENVIRONMENTS_ENDPOINT;

/**
* The default URI for the accounts endpoint.
*/
public final URI DEFAULT_ACCOUNTS_ENDPOINT;
/**
* Constructs an instance of {@link EnvVarSdkConfiguration}.
*
Expand All @@ -38,6 +42,7 @@ public EnvVarSdkConfiguration() throws URISyntaxException {
DEFAULT_LIVESYSTEM_ENDPOINT = new URI("https://api.fractal.cloud/livesystems");
DEFAULT_BLUEPRINT_ENDPOINT = new URI("https://api.fractal.cloud/blueprints");
DEFAULT_ENVIRONMENTS_ENDPOINT = new URI("https://api.fractal.cloud/environments");
DEFAULT_ACCOUNTS_ENDPOINT = new URI("http://localhost:5001/accounts");
}

/**
Expand Down Expand Up @@ -129,6 +134,14 @@ public URI getEnvironmentsEndpoint() {
return checkAndReturnUri(ENVIRONMENTS_ENDPOINT_KEY, DEFAULT_ENVIRONMENTS_ENDPOINT);
}

/**
* Gets the URI of the accounts endpoint from environment variables or default.
* @return the URI of the accounts endpoint
*/
@Override
public URI getAccountsEndpoint(){
return checkAndReturnUri(ACCOUNTS_ENDPOINT_KEY, DEFAULT_ACCOUNTS_ENDPOINT);
}
/**
* Gets the AWS Access Key ID, part of the temporary credentials of an AWS role, from environment variables.
* The environment variable key is {@value com.yanchware.fractal.sdk.configuration.Constants#AWS_ACCESS_KEY_ID_KEY}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ public interface SdkConfiguration {
*/
URI getEnvironmentsEndpoint();

/**
* Gets the URI of the accounts endpoint.
*
* @return the URI of the accounts endpoint
*/
URI getAccountsEndpoint();

/**
* Gets the AWS Access Key ID.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.yanchware.fractal.sdk.domain.accounts;

import com.yanchware.fractal.sdk.domain.accounts.service.AccountsService;
import com.yanchware.fractal.sdk.domain.accounts.service.RestAccountsService;
import com.yanchware.fractal.sdk.domain.accounts.service.dtos.PersonalResourceGroupResponse;
import com.yanchware.fractal.sdk.domain.exceptions.InstantiatorException;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Setter(AccessLevel.PROTECTED)
public class AccountAggregate {

private final AccountsService service;

public record PersonalResourceGroup(String shortName, String displayName) {
}

private final List<PersonalResourceGroup> personalResourceGroups = new ArrayList<>();

public AccountAggregate(RestAccountsService restAccountsService) {
this.service = restAccountsService;
}

public void addPersonalResourceGroup(String shortName, String displayName) {
int idx = -1;
for (int i = 0; i < personalResourceGroups.size(); i++) {
if (personalResourceGroups.get(i).shortName().equals(shortName)) {
idx = i;
break;
}
}
if (idx >= 0) {
var existing = personalResourceGroups.get(idx);
if (!existing.displayName().equals(displayName)) {
personalResourceGroups.set(idx, new PersonalResourceGroup(shortName, displayName));
}
return;
}
personalResourceGroups.add(new PersonalResourceGroup(shortName, displayName));
}


public void createOrUpdate() throws InstantiatorException {
if (personalResourceGroups.isEmpty()) {
log.info("No personal resource groups to manage.");
return;
}

int created = 0;
for (var rg : personalResourceGroups) {
var shortName = rg.shortName();
var displayName = rg.displayName();

PersonalResourceGroupResponse existing = service.getPersonalResourceGroupByShortName(shortName);

if (existing == null) {
log.info("Creating Personal Resource Group [shortName: '{}', displayName: '{}']", shortName, displayName);
var response = service.upsertPersonalResourceGroup(shortName, displayName);
log.debug("Upserted Personal Resource Group id: {}", response.Id());
created++;
} else {
log.info("Personal Resource Group [shortName: '{}'] already exists. Skipping.", shortName);
}
}
log.info("Created {} new Personal Resource Group(s).", created);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.yanchware.fractal.sdk.domain.accounts;

import com.yanchware.fractal.sdk.configuration.SdkConfiguration;
import com.yanchware.fractal.sdk.domain.accounts.service.RestAccountsService;
import io.github.resilience4j.retry.RetryRegistry;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.net.http.HttpClient;

@Slf4j
@AllArgsConstructor
public class AccountsFactory {
private final HttpClient client;
private final SdkConfiguration sdkConfiguration;
private final RetryRegistry retryRegistry;

public AccountBuilder builder() {
return new AccountBuilder(client, sdkConfiguration, retryRegistry);
}

public static class AccountBuilder {
private final AccountAggregate aggregate;
private final AccountBuilder builder;

public AccountBuilder(HttpClient client, SdkConfiguration sdkConfiguration, RetryRegistry retryRegistry) {
aggregate = create(client, sdkConfiguration, retryRegistry);
builder = getBuilder();
}

protected AccountAggregate create(HttpClient client, SdkConfiguration sdkConfiguration, RetryRegistry retryRegistry) {
return new AccountAggregate(new RestAccountsService(client, sdkConfiguration, retryRegistry));
}

protected AccountsFactory.AccountBuilder getBuilder() {
return this;
}

public AccountBuilder withResourceGroup(String shortName, String displayName) {
aggregate.addPersonalResourceGroup(shortName, displayName);
return builder;
}

public AccountAggregate build() {
return aggregate;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.yanchware.fractal.sdk.domain.accounts;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum EntityStatus {
UNKNOWN("Unknown"),
ACTIVE("Active"),
DELETING("Deleting");

private final String value;

EntityStatus(String value) {
this.value = value;
}

@JsonValue
public String getValue() {
return value;
}

@Override
public String toString() {
return value;
}

@JsonCreator
public static EntityStatus fromString(String s) {
if (s == null) return null;
for (var e : values()) {
if (e.value.equalsIgnoreCase(s) || e.name().equalsIgnoreCase(s)) {
return e;
}
}
throw new IllegalArgumentException("Invalid EntityStatus: " + s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.yanchware.fractal.sdk.domain.accounts;

import com.yanchware.fractal.sdk.domain.accounts.service.AccountsService;
import com.yanchware.fractal.sdk.domain.accounts.service.RestAccountsService;
import com.yanchware.fractal.sdk.domain.accounts.service.dtos.OrganizationalResourceGroupResponse;
import com.yanchware.fractal.sdk.domain.exceptions.InstantiatorException;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Slf4j
@Setter(AccessLevel.PROTECTED)
public class OrganizationAggregate {

private final AccountsService service;

public record OrganizationalResourceGroup(UUID organizationId, String shortName, String displayName) {}

private final List<OrganizationalResourceGroup> organizationalResourceGroups = new ArrayList<>();

public OrganizationAggregate(RestAccountsService restAccountsService) {
this.service = restAccountsService;
}

public void addOrganizationalResourceGroup(UUID organizationId, String shortName, String displayName) {
int idx = -1;
for (int i = 0; i < organizationalResourceGroups.size(); i++) {
var item = organizationalResourceGroups.get(i);
if (item.organizationId().equals(organizationId) && item.shortName().equals(shortName)) {
idx = i;
break;
}
}
if (idx >= 0) {
var existing = organizationalResourceGroups.get(idx);
if (!existing.displayName().equals(displayName)) {
organizationalResourceGroups.set(idx, new OrganizationalResourceGroup(organizationId, shortName, displayName));
}
return;
}
organizationalResourceGroups.add(new OrganizationalResourceGroup(organizationId, shortName, displayName));
}

public void createOrUpdate() throws InstantiatorException {
if (organizationalResourceGroups.isEmpty()) {
log.info("No organizational resource groups to manage.");
return;
}

int created = 0;
for (var rg : organizationalResourceGroups) {
var organizationId = rg.organizationId();
var shortName = rg.shortName();
var displayName = rg.displayName();

OrganizationalResourceGroupResponse existing =
service.getOrganizationalResourceGroupByShortName(organizationId, shortName);

if (existing == null) {
log.info("Creating Organizational Resource Group [organizationId: '{}', shortName: '{}', displayName: '{}']",
organizationId, shortName, displayName);

var response = service.upsertOrganizationalResourceGroup(organizationId, shortName, displayName);
if (response != null) {
log.debug("Upserted Organizational Resource Group id: {}", response.Id());
}
created++;
} else {
log.info("Organizational Resource Group [organizationId: '{}', shortName: '{}'] already exists. Skipping.",
organizationId, shortName);
}
}
log.info("Created {} new Organizational Resource Group(s).", created);
}
}
Loading
Loading