diff --git a/pom.xml b/pom.xml index b1d01889..8a33bc3a 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.4 + 3.2.5 @@ -609,7 +609,7 @@ org.springframework.boot spring-boot-maven-plugin - 3.2.4 + 3.2.5 paketobuildpacks/builder-jammy-tiny diff --git a/samples/application-pcfone.yml b/samples/application-pcfone.yml index 76b83db5..d1e4ddbf 100644 --- a/samples/application-pcfone.yml +++ b/samples/application-pcfone.yml @@ -14,7 +14,6 @@ logging: # @see https://crontab.guru for help, first parameter is seconds cron: collection: "0 0 0 * * *" - execution: "0 0 2 * * *" management: endpoints: diff --git a/samples/application-pws.yml b/samples/application-pws.yml index ff415fb2..874198da 100644 --- a/samples/application-pws.yml +++ b/samples/application-pws.yml @@ -14,7 +14,6 @@ logging: # @see https://crontab.guru for help, first parameter is seconds cron: collection: "0 0 0 * * *" - execution: "0 0 2 * * *" management: endpoints: diff --git a/samples/secrets.pcfone.json b/samples/secrets.pcfone.json index e7f4a3ba..8329089e 100644 --- a/samples/secrets.pcfone.json +++ b/samples/secrets.pcfone.json @@ -5,6 +5,5 @@ "CF_REFRESH-TOKEN": "xxxxxx", "CF_ORGANIZATION-BLACK-LIST": [ "system" ], "CF_ACCOUNT-REGEX": "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$", - "CRON_COLLECTION": "0 0 0 * * *", - "CRON_EXECUTION": "0 0 2 * * *" + "CRON_COLLECTION": "0 0 0 * * *" } \ No newline at end of file diff --git a/samples/secrets.pws.json b/samples/secrets.pws.json index 6e78a41e..ab8e4431 100644 --- a/samples/secrets.pws.json +++ b/samples/secrets.pws.json @@ -7,7 +7,6 @@ "CF_ORGANIZATION-BLACK-LIST": [ "system" ], "CF_ACCOUNT-REGEX": "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$", "CRON_COLLECTION": "0 0 0 * * *", - "CRON_EXECUTION": "0 0 2 * * *", "CF_POLICIES_GIT_URI": "https://github.com/cf-toolsuite/cf-butler-sample-config.git", "CF_POLICIES_GIT_COMMIT": "e745cfe5d93e6517038675fd6b0fe3b85524f130", "CF_POLICIES_GIT_FILE-PATHS": [ diff --git a/samples/secrets.pws.with-mysql.json b/samples/secrets.pws.with-mysql.json index 9d45d5c1..dc98e018 100644 --- a/samples/secrets.pws.with-mysql.json +++ b/samples/secrets.pws.with-mysql.json @@ -7,7 +7,6 @@ "CF_ORGANIZATION-BLACK-LIST": [ "system" ], "CF_ACCOUNT-REGEX": "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$", "CRON_COLLECTION": "0 0 0 * * *", - "CRON_EXECUTION": "0 0 2 * * *", "CF_POLICIES_GIT_URI": "https://github.com/cf-toolsuite/cf-butler-sample-config.git", "CF_POLICIES_GIT_COMMIT": "e745cfe5d93e6517038675fd6b0fe3b85524f130", "CF_POLICIES_GIT_FILE-PATHS": [ diff --git a/samples/secrets.pws.with-postgres.json b/samples/secrets.pws.with-postgres.json index 617f564b..410771ad 100644 --- a/samples/secrets.pws.with-postgres.json +++ b/samples/secrets.pws.with-postgres.json @@ -7,7 +7,6 @@ "CF_ORGANIZATION-BLACK-LIST": [ "system" ], "CF_ACCOUNT-REGEX": "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$", "CRON_COLLECTION": "0 0 0 * * *", - "CRON_EXECUTION": "0 0 2 * * *", "CF_POLICIES_GIT_URI": "https://github.com/cf-toolsuite/cf-butler-sample-config.git", "CF_POLICIES_GIT_COMMIT": "e745cfe5d93e6517038675fd6b0fe3b85524f130", "CF_POLICIES_GIT_FILE-PATHS": [ diff --git a/src/main/java/org/cftoolsuite/cfapp/AppInit.java b/src/main/java/org/cftoolsuite/cfapp/AppInit.java index 1af4c43a..73be77b0 100644 --- a/src/main/java/org/cftoolsuite/cfapp/AppInit.java +++ b/src/main/java/org/cftoolsuite/cfapp/AppInit.java @@ -3,14 +3,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; import io.netty.util.ResourceLeakDetector; import reactor.core.publisher.Hooks; -@EnableScheduling @EnableTransactionManagement @ConfigurationPropertiesScan @SpringBootApplication diff --git a/src/main/java/org/cftoolsuite/cfapp/config/ButlerCfEnvProcessor.java b/src/main/java/org/cftoolsuite/cfapp/config/ButlerCfEnvProcessor.java index 06ce4361..e2e3fa74 100644 --- a/src/main/java/org/cftoolsuite/cfapp/config/ButlerCfEnvProcessor.java +++ b/src/main/java/org/cftoolsuite/cfapp/config/ButlerCfEnvProcessor.java @@ -79,7 +79,6 @@ public void process(CfCredentials cfCredentials, Map properties) addOrUpdatePropertyValue("pivnet.apiToken", "PIVNET_API-TOKEN", cfCredentials, properties); addOrUpdatePropertyValue("pivnet.enabled", "PIVNET_ENABLED", cfCredentials, properties); addOrUpdatePropertyValue("cron.collection", "CRON_COLLECTION", cfCredentials, properties); - addOrUpdatePropertyValue("cron.execution", "CRON_EXECUTION", cfCredentials, properties); addOrUpdatePropertyValue("management.endpoints.web.exposure.include", "EXPOSED_ACTUATOR_ENDPOINTS", cfCredentials, properties); addOrUpdatePropertyValue("java.artifacts.fetch.mode", "JAVA_ARTIFACTS_FETCH_MODE", cfCredentials, properties); } diff --git a/src/main/java/org/cftoolsuite/cfapp/controller/OnDemandPolicyTriggerController.java b/src/main/java/org/cftoolsuite/cfapp/controller/OnDemandPolicyTriggerController.java index 6a5f0bf8..51eb5966 100644 --- a/src/main/java/org/cftoolsuite/cfapp/controller/OnDemandPolicyTriggerController.java +++ b/src/main/java/org/cftoolsuite/cfapp/controller/OnDemandPolicyTriggerController.java @@ -1,35 +1,42 @@ package org.cftoolsuite.cfapp.controller; -import java.util.Collection; -import java.util.Map; - +import org.cftoolsuite.cfapp.service.PoliciesService; import org.cftoolsuite.cfapp.task.PolicyExecutorTask; -import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Profile("on-demand") @RestController public class OnDemandPolicyTriggerController { - private ListableBeanFactory factory; + private BeanFactory factory; + private PoliciesService service; @Autowired - public OnDemandPolicyTriggerController(ListableBeanFactory factory) { + public OnDemandPolicyTriggerController(BeanFactory factory, PoliciesService service) { this.factory = factory; + this.service = service; } @PostMapping("/policies/execute") - public Mono> triggerPolicyExection() { - Map taskMap = factory.getBeansOfType(PolicyExecutorTask.class); - Collection tasks = taskMap.values(); - tasks.forEach(PolicyExecutorTask::execute); - return Mono.just(ResponseEntity.accepted().build()); + public Mono> triggerPolicyExecution() { + return service.getTaskMap() + .flatMapMany(taskTypeMap -> + Flux.fromIterable(taskTypeMap.entrySet()) + .flatMap(entry -> { + String policyId = entry.getKey(); + Class taskClass = entry.getValue(); + PolicyExecutorTask task = factory.getBean(taskClass); + return Mono.fromRunnable(() -> task.execute(policyId)); + })) + .then(Mono.just(ResponseEntity.accepted().build())); } } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationOperation.java b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationOperation.java index 45baa988..7509e676 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationOperation.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationOperation.java @@ -1,8 +1,15 @@ package org.cftoolsuite.cfapp.domain; import java.util.Arrays; +import java.util.EnumMap; +import java.util.Map; import java.util.stream.Collectors; +import org.cftoolsuite.cfapp.task.DeleteAppPolicyExecutorTask; +import org.cftoolsuite.cfapp.task.PolicyExecutorTask; +import org.cftoolsuite.cfapp.task.ScaleAppInstancesPolicyExecutorTask; +import org.cftoolsuite.cfapp.task.StackChangeAppInstancesPolicyExecutorTask; +import org.cftoolsuite.cfapp.task.StopAppPolicyExecutorTask; import org.springframework.util.Assert; import com.fasterxml.jackson.annotation.JsonValue; @@ -14,6 +21,20 @@ public enum ApplicationOperation { STOP("stop"), CHANGE_STACK("change-stack"); + private final String name; + + ApplicationOperation(String name) { + this.name = name; + } + + static final Map> operationTaskMap = new EnumMap<>(ApplicationOperation.class); + static { + operationTaskMap.put(ApplicationOperation.DELETE, DeleteAppPolicyExecutorTask.class); + operationTaskMap.put(ApplicationOperation.SCALE_INSTANCES, ScaleAppInstancesPolicyExecutorTask.class); + operationTaskMap.put(ApplicationOperation.STOP, StopAppPolicyExecutorTask.class); + operationTaskMap.put(ApplicationOperation.CHANGE_STACK, StackChangeAppInstancesPolicyExecutorTask.class); + } + public static ApplicationOperation from(String name) { Assert.hasText(name, "ApplicationOperation must not be null or empty"); ApplicationOperation result = Arrays.asList(ApplicationOperation.values()).stream().filter(s -> s.getName().equalsIgnoreCase(name)).collect(Collectors.toList()).get(0); @@ -21,14 +42,13 @@ public static ApplicationOperation from(String name) { return result; } - private final String name; - - ApplicationOperation(String name) { - this.name = name; + public static Class getTaskType(String op) { + return operationTaskMap.get(from(op)); } @JsonValue public String getName() { return name; } + } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicy.java b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicy.java index a7b19507..144d6da4 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicy.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicy.java @@ -27,15 +27,16 @@ @Builder @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({ "id", "operation", "description", "state", "options", "organization-whitelist" }) +@JsonPropertyOrder({ "id", "git-commit", "operation", "description", "state", "options", "organization-whitelist", "cron-expression" }) @Getter @ToString @Table("application_policy") -public class ApplicationPolicy implements HasOrganizationWhiteList { +public class ApplicationPolicy implements HasOrganizationWhiteList, Policy { public static ApplicationPolicy seed(ApplicationPolicy policy) { return ApplicationPolicy .builder() + .cronExpression(policy.getCronExpression()) .description(policy.getDescription()) .operation(policy.getOperation()) .options(policy.getOptions()) @@ -44,10 +45,11 @@ public static ApplicationPolicy seed(ApplicationPolicy policy) { .build(); } - public static ApplicationPolicy seedWith(ApplicationPolicy policy, String id) { + public static ApplicationPolicy seedWith(ApplicationPolicy policy, String gitCommit) { return ApplicationPolicy .builder() - .id(id) + .gitCommit(gitCommit) + .cronExpression(policy.getCronExpression()) .description(policy.getDescription()) .operation(policy.getOperation()) .options(policy.getOptions()) @@ -64,6 +66,10 @@ public static ApplicationPolicy seedWith(ApplicationPolicy policy, String id) { @JsonProperty("id") private String id = Generators.timeBasedGenerator().generate().toString(); + @JsonProperty("git-commit") + @Column("git_commit") + private String gitCommit; + @JsonProperty("operation") private String operation; @@ -82,22 +88,30 @@ public static ApplicationPolicy seedWith(ApplicationPolicy policy, String id) { @Column("organization_whitelist") private Set organizationWhiteList = new HashSet<>(); + @JsonProperty("cron-expression") + @Column("cron_expression") + private String cronExpression; + @JsonCreator ApplicationPolicy( @JsonProperty("pk") Long pk, @JsonProperty("id") String id, + @JsonProperty("git-commit") String gitCommit, @JsonProperty("operation") String operation, @JsonProperty("description") String description, @JsonProperty("state") String state, @JsonProperty("options") Map options, - @JsonProperty("organization-whitelist") Set organizationWhiteList) { + @JsonProperty("organization-whitelist") Set organizationWhiteList, + @JsonProperty("cron-expression") String cronExpression) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.operation = operation; this.description = description; this.state = state; this.options = options; this.organizationWhiteList = organizationWhiteList; + this.cronExpression = cronExpression; } @JsonIgnore @@ -110,6 +124,10 @@ public T getOption(String key, Class type) { return type.cast(value); } + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; + } + public Map getOptions() { return CollectionUtils.isEmpty(options) ? new HashMap<>(): Collections.unmodifiableMap(options); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyReadConverter.java index 38e2c73e..5231589f 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyReadConverter.java @@ -26,10 +26,12 @@ public ApplicationPolicy convert(Row source) { .builder() .pk(source.get("pk", Long.class)) .id(source.get("id", String.class)) + .gitCommit(source.get("git_commit", String.class)) .operation(source.get("operation", String.class)) .description(source.get("description", String.class)) .options(readOptions(source.get("options", String.class) == null ? "{}" : source.get("options", String.class))) .organizationWhiteList(CsvUtil.parse(source.get("organization_whitelist", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .state(source.get("state", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyWriteConverter.java index 8a5c07f5..825079ed 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ApplicationPolicyWriteConverter.java @@ -23,10 +23,12 @@ public class ApplicationPolicyWriteConverter implements Converter endpoints, - @JsonProperty("email-notification-template") EmailNotificationTemplate emailNotificationTemplate) { + @JsonProperty("email-notification-template") EmailNotificationTemplate emailNotificationTemplate, + @JsonProperty("cron-expression") String cronExpression) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.description = description; this.endpoints = endpoints; this.emailNotificationTemplate = emailNotificationTemplate; + this.cronExpression = cronExpression; + } + + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; } public Set getEndpoints() { @@ -87,5 +107,4 @@ public Set getEndpoints() { public Long getPk() { return pk; } - } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyReadConverter.java index 1abedbc1..f301a740 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyReadConverter.java @@ -32,6 +32,7 @@ public EndpointPolicy convert(Row source) { source.get("email_notification_template", String.class) == null ? "{}" : source.get("email_notification_template", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyWriteConverter.java index cb9d5929..3e91fa13 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/EndpointPolicyWriteConverter.java @@ -22,9 +22,11 @@ public class EndpointPolicyWriteConverter implements Converter organizationWhiteList = new HashSet<>(); + @JsonProperty("cron-expression") + @Column("cron_expression") + private String cronExpression; + @Default @JsonProperty("include-applications") @Column("include_applications") @@ -93,23 +104,31 @@ public static HygienePolicy seedWith(HygienePolicy policy, String id) { public HygienePolicy( @JsonProperty("pk") Long pk, @JsonProperty("id") String id, + @JsonProperty("git-commit") String gitCommit, @JsonProperty("days-since-last-update") Integer daysSinceLastUpdate, @JsonProperty("operator-email-template") EmailNotificationTemplate operatorTemplate, @JsonProperty("notifyee-email-template") EmailNotificationTemplate notifyeeTemplate, @JsonProperty("organization-whitelist") Set organizationWhiteList, + @JsonProperty("cron-expression") String cronExpression, @JsonProperty("include-applications") boolean includeApplications, @JsonProperty("include-service-instances") boolean includeServiceInstances ) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.daysSinceLastUpdate = daysSinceLastUpdate; this.operatorTemplate = operatorTemplate; this.notifyeeTemplate = notifyeeTemplate; this.organizationWhiteList = organizationWhiteList; + this.cronExpression = cronExpression; this.includeApplications = includeApplications; this.includeServiceInstances = includeServiceInstances; } + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; + } + public Set getOrganizationWhiteList() { return CollectionUtils.isEmpty(organizationWhiteList) ? new HashSet<>() : Collections.unmodifiableSet(organizationWhiteList); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyReadConverter.java index f10d890c..60aba022 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyReadConverter.java @@ -24,10 +24,12 @@ public HygienePolicy convert(Row source) { .builder() .pk(source.get("pk", Long.class)) .id(source.get("id", String.class)) + .gitCommit(source.get("git_commit", String.class)) .daysSinceLastUpdate(source.get("days_since_last_update", Integer.class)) .operatorTemplate(readEmailNotificationTemplate(source.get("operator_email_template", String.class) == null ? "{}": source.get("operator_email_template", String.class))) .notifyeeTemplate(readEmailNotificationTemplate(source.get("notifyee_email_template", String.class) == null ? "{}": source.get("notifyee_email_template", String.class))) .organizationWhiteList(CsvUtil.parse(source.get("organization_whitelist", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyWriteConverter.java index 328b616f..8451e409 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/HygienePolicyWriteConverter.java @@ -22,10 +22,12 @@ public class HygienePolicyWriteConverter implements Converter stacks = new HashSet<>(); @@ -79,23 +86,35 @@ public static LegacyPolicy seedWith(LegacyPolicy policy, String id) { @Column("organization_whitelist") private Set organizationWhiteList = new HashSet<>(); + @JsonProperty("cron-expression") + @Column("cron_expression") + private String cronExpression; + @JsonCreator public LegacyPolicy( @JsonProperty("pk") Long pk, @JsonProperty("id") String id, + @JsonProperty("git-commit") String gitCommit, @JsonProperty("stacks") Set stacks, @JsonProperty("service-offerings") Set serviceOfferings, @JsonProperty("operator-email-template") EmailNotificationTemplate operatorTemplate, @JsonProperty("notifyee-email-template") EmailNotificationTemplate notifyeeTemplate, - @JsonProperty("organization-whitelist") Set organizationWhiteList + @JsonProperty("organization-whitelist") Set organizationWhiteList, + @JsonProperty("cron-expression") String cronExpression ) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.stacks = stacks; this.serviceOfferings = serviceOfferings; this.operatorTemplate = operatorTemplate; this.notifyeeTemplate = notifyeeTemplate; this.organizationWhiteList = organizationWhiteList; + this.cronExpression = cronExpression; + } + + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; } public Set getOrganizationWhiteList() { diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyReadConverter.java index 01bc6ce0..fabc47b9 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyReadConverter.java @@ -24,11 +24,13 @@ public LegacyPolicy convert(Row source) { .builder() .pk(source.get("pk", Long.class)) .id(source.get("id", String.class)) + .gitCommit(source.get("git_commit", String.class)) .stacks(CsvUtil.parse(source.get("stacks", String.class))) .serviceOfferings(CsvUtil.parse(source.get("service_offerings", String.class))) .operatorTemplate(readEmailNotificationTemplate(source.get("operator_email_template", String.class) == null ? "{}": source.get("operator_email_template", String.class))) .notifyeeTemplate(readEmailNotificationTemplate(source.get("notifyee_email_template", String.class) == null ? "{}": source.get("notifyee_email_template", String.class))) .organizationWhiteList(CsvUtil.parse(source.get("organization_whitelist", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyWriteConverter.java index 9b5f670c..8a0c0654 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/LegacyPolicyWriteConverter.java @@ -22,11 +22,13 @@ public class LegacyPolicyWriteConverter implements Converter getResourceNotificationPolicies() { return resourceNotificationPolicies != null ? resourceNotificationPolicies: Collections.emptyList(); } + public List all() { + List policies = new ArrayList<>(); + policies.addAll(getApplicationPolicies()); + policies.addAll(getEndpointPolicies()); + policies.addAll(getHygienePolicies()); + policies.addAll(getLegacyPolicies()); + policies.addAll(getQueryPolicies()); + policies.addAll(getResourceNotificationPolicies()); + policies.addAll(getServiceInstancePolicies()); + return policies; + } + + public Policy getById(String policyId) { + return all() + .stream() + .filter(policy -> policy.getId().equals(policyId)) + .findFirst() + .orElse(null); + } + @JsonIgnore public boolean isEmpty() { return getApplicationPolicies().isEmpty() diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/Policy.java b/src/main/java/org/cftoolsuite/cfapp/domain/Policy.java new file mode 100644 index 00000000..04964712 --- /dev/null +++ b/src/main/java/org/cftoolsuite/cfapp/domain/Policy.java @@ -0,0 +1,3 @@ +package org.cftoolsuite.cfapp.domain; + +public interface Policy extends HasIdentifier, HasCronExpression {} diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicy.java b/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicy.java index dc333afb..58949b1d 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicy.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicy.java @@ -4,7 +4,9 @@ import java.util.HashSet; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; import org.springframework.util.CollectionUtils; @@ -22,11 +24,11 @@ @Builder @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({ "id", "description", "queries", "email-notification-template" }) +@JsonPropertyOrder({ "id", "git-commit", "description", "queries", "email-notification-template", "cron-expression" }) @Getter @ToString @Table("query_policy") -public class QueryPolicy { +public class QueryPolicy implements Policy { public static QueryPolicy seed(QueryPolicy policy) { return QueryPolicy @@ -34,16 +36,18 @@ public static QueryPolicy seed(QueryPolicy policy) { .description(policy.getDescription()) .queries(policy.getQueries()) .emailNotificationTemplate(policy.getEmailNotificationTemplate()) + .cronExpression(policy.getCronExpression()) .build(); } - public static QueryPolicy seedWith(QueryPolicy policy, String id) { + public static QueryPolicy seedWith(QueryPolicy policy, String gitCommit) { return QueryPolicy .builder() - .id(id) + .gitCommit(gitCommit) .description(policy.getDescription()) .queries(policy.getQueries()) .emailNotificationTemplate(policy.getEmailNotificationTemplate()) + .cronExpression(policy.getCronExpression()) .build(); } @@ -55,6 +59,10 @@ public static QueryPolicy seedWith(QueryPolicy policy, String id) { @JsonProperty("id") private String id = Generators.timeBasedGenerator().generate().toString(); + @JsonProperty("git-commit") + @Column("git_commit") + private String gitCommit; + @JsonProperty("description") private String description; @@ -65,18 +73,30 @@ public static QueryPolicy seedWith(QueryPolicy policy, String id) { @JsonProperty("email-notification-template") private EmailNotificationTemplate emailNotificationTemplate; + @JsonProperty("cron-expression") + @Column("cron_expression") + private String cronExpression; + @JsonCreator QueryPolicy( @JsonProperty("pk") Long pk, @JsonProperty("id") String id, + @JsonProperty("git-commit") String gitCommit, @JsonProperty("description") String description, @JsonProperty("queries") Set queries, - @JsonProperty("email-notification-template") EmailNotificationTemplate emailNotificationTemplate) { + @JsonProperty("email-notification-template") EmailNotificationTemplate emailNotificationTemplate, + @JsonProperty("cron-expression") String cronExpression) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.description = description; this.queries = queries; this.emailNotificationTemplate = emailNotificationTemplate; + this.cronExpression = cronExpression; + } + + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; } @JsonIgnore diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyReadConverter.java index 45d5f24b..a8a659fd 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyReadConverter.java @@ -25,6 +25,7 @@ public QueryPolicy convert(Row source) { .builder() .pk(source.get("pk", Long.class)) .id(source.get("id", String.class)) + .gitCommit(source.get("git_commit", String.class)) .description(source.get("description", String.class)) .queries(readQueries(source.get("queries", String.class) == null ? "[]" : source.get("queries", String.class))) .emailNotificationTemplate( @@ -32,6 +33,7 @@ public QueryPolicy convert(Row source) { source.get("email_notification_template", String.class) == null ? "{}" : source.get("email_notification_template", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyWriteConverter.java index 0df66a8e..625d6562 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/QueryPolicyWriteConverter.java @@ -22,9 +22,11 @@ public class QueryPolicyWriteConverter implements Converter resourceBlackList = new HashSet<>(); + @JsonProperty("cron-expression") + @Column("cron_expression") + private String cronExpression; + @JsonCreator public ResourceNotificationPolicy( @JsonProperty("pk") Long pk, @JsonProperty("id") String id, + @JsonProperty("git-commit") String gitCommit, @JsonProperty("resource-email-template") EmailNotificationTemplate resourceEmailTemplate, @JsonProperty("resource-email-metadata") ResourceEmailMetadata resourceEmailMetadata, @JsonProperty("resource-whitelist") Set resourceWhiteList, - @JsonProperty("resource-blacklist") Set resourceBlackList + @JsonProperty("resource-blacklist") Set resourceBlackList, + @JsonProperty("cron-expression") String cronExpression ) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.resourceEmailTemplate = resourceEmailTemplate; this.resourceEmailMetadata = resourceEmailMetadata; this.resourceWhiteList = resourceWhiteList; this.resourceBlackList = resourceBlackList; + this.cronExpression = cronExpression; + } + + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; + } + + @JsonIgnore + public Long getPk() { + return pk; } public Set getResourceWhiteList() { @@ -100,8 +124,4 @@ public Set getResourceBlackList() { return CollectionUtils.isEmpty(resourceBlackList) ? new HashSet<>() : Collections.unmodifiableSet(resourceBlackList); } - @JsonIgnore - public Long getPk() { - return pk; - } } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyReadConverter.java index f39a8dac..eb5907df 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyReadConverter.java @@ -24,10 +24,12 @@ public ResourceNotificationPolicy convert(Row source) { .builder() .pk(source.get("pk", Long.class)) .id(source.get("id", String.class)) + .gitCommit(source.get("git_commit", String.class)) .resourceEmailTemplate(readEmailNotificationTemplate(source.get("resource_email_template", String.class) == null ? "{}": source.get("resource_email_template", String.class))) .resourceEmailMetadata(readResourceEmailMetadata(source.get("resource_email_metadata", String.class) == null ? "{}": source.get("resource_email_metadata", String.class))) .resourceWhiteList(CsvUtil.parse(source.get("resource_whitelist", String.class))) .resourceBlackList(CsvUtil.parse(source.get("resource_blacklist", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyWriteConverter.java index c1421117..49ae45f6 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ResourceNotificationPolicyWriteConverter.java @@ -22,10 +22,12 @@ public class ResourceNotificationPolicyWriteConverter implements Converter> operationTaskMap = new EnumMap<>(ServiceInstanceOperation.class); + static { + operationTaskMap.put(ServiceInstanceOperation.DELETE, DeleteServiceInstancePolicyExecutorTask.class); + } + public static ServiceInstanceOperation from(String name) { Assert.hasText(name, "ServiceInstanceOperation must not be null or empty"); ServiceInstanceOperation result = Arrays.asList(ServiceInstanceOperation.values()).stream().filter(s -> s.getName().equalsIgnoreCase(name)).collect(Collectors.toList()).get(0); @@ -18,10 +33,8 @@ public static ServiceInstanceOperation from(String name) { return result; } - private final String name; - - ServiceInstanceOperation(String name) { - this.name = name; + public static Class getTaskType(String op) { + return operationTaskMap.get(from(op)); } @JsonValue diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicy.java b/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicy.java index 8df86d15..6d0ac21a 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicy.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicy.java @@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -25,17 +26,10 @@ @Builder @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonPropertyOrder({ "id", "operation", "description", "options", "organization-whitelist" }) +@JsonPropertyOrder({ "id", "git-commit", "operation", "description", "options", "organization-whitelist", "cron-expression" }) @Getter @ToString -public class ServiceInstancePolicy implements HasOrganizationWhiteList { - - public static String[] columnNames() { - return - new String[] { - "pk", "id", "operation", "description", "options", "organization_whitelist" - }; - } +public class ServiceInstancePolicy implements HasOrganizationWhiteList, Policy { public static ServiceInstancePolicy seed(ServiceInstancePolicy policy) { return ServiceInstancePolicy @@ -44,17 +38,19 @@ public static ServiceInstancePolicy seed(ServiceInstancePolicy policy) { .operation(policy.getOperation()) .options(policy.getOptions()) .organizationWhiteList(policy.getOrganizationWhiteList()) + .cronExpression(policy.getCronExpression()) .build(); } - public static ServiceInstancePolicy seedWith(ServiceInstancePolicy policy, String id) { + public static ServiceInstancePolicy seedWith(ServiceInstancePolicy policy, String gitCommit) { return ServiceInstancePolicy .builder() - .id(id) + .gitCommit(gitCommit) .description(policy.getDescription()) .operation(policy.getOperation()) .options(policy.getOptions()) .organizationWhiteList(policy.getOrganizationWhiteList()) + .cronExpression(policy.getCronExpression()) .build(); } @@ -70,6 +66,10 @@ public static String tableName() { @JsonProperty("id") private String id = Generators.timeBasedGenerator().generate().toString(); + @JsonProperty("git-commit") + @Column("git_commit") + private String gitCommit; + @JsonProperty("operation") private String operation; @@ -84,20 +84,32 @@ public static String tableName() { @JsonProperty("organization-whitelist") private Set organizationWhiteList = new HashSet<>(); + @JsonProperty("cron-expression") + @Column("cron_expression") + private String cronExpression; + @JsonCreator ServiceInstancePolicy( @JsonProperty("pk") Long pk, @JsonProperty("id") String id, + @JsonProperty("git-commit") String gitCommit, @JsonProperty("operation") String operation, @JsonProperty("description") String description, @JsonProperty("options") Map options, - @JsonProperty("organization-whitelist") Set organizationWhiteList) { + @JsonProperty("organization-whitelist") Set organizationWhiteList, + @JsonProperty("cron-expression") String cronExpression) { this.pk = pk; this.id = id; + this.gitCommit = gitCommit; this.operation = operation; this.description = description; this.options = options; this.organizationWhiteList = organizationWhiteList; + this.cronExpression = cronExpression; + } + + public String getCronExpression() { + return StringUtils.isBlank(cronExpression) ? defaultCronExpression(): cronExpression; } @JsonIgnore diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyReadConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyReadConverter.java index ee8157a5..2bda66d6 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyReadConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyReadConverter.java @@ -26,10 +26,12 @@ public ServiceInstancePolicy convert(Row source) { .builder() .pk(source.get("pk", Long.class)) .id(source.get("id", String.class)) + .gitCommit(source.get("git_commit", String.class)) .operation(source.get("operation", String.class)) .description(source.get("description", String.class)) .options(readOptions(source.get("options", String.class) == null ? "{}" : source.get("options", String.class))) .organizationWhiteList(CsvUtil.parse(source.get("organization_whitelist", String.class))) + .cronExpression(source.get("cron_expression", String.class)) .build(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyWriteConverter.java b/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyWriteConverter.java index dfe3816b..bb8712cc 100644 --- a/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyWriteConverter.java +++ b/src/main/java/org/cftoolsuite/cfapp/domain/ServiceInstancePolicyWriteConverter.java @@ -23,10 +23,12 @@ public class ServiceInstancePolicyWriteConverter implements Converter findQueryPolicyById(String id); Mono findServiceInstancePolicyById(String id); Mono save(Policies entity); + Mono>> getTaskMap(); } diff --git a/src/main/java/org/cftoolsuite/cfapp/service/R2dbcPoliciesService.java b/src/main/java/org/cftoolsuite/cfapp/service/R2dbcPoliciesService.java index 06382368..f0323dd2 100644 --- a/src/main/java/org/cftoolsuite/cfapp/service/R2dbcPoliciesService.java +++ b/src/main/java/org/cftoolsuite/cfapp/service/R2dbcPoliciesService.java @@ -1,13 +1,30 @@ package org.cftoolsuite.cfapp.service; +import java.util.HashMap; +import java.util.Map; + import org.cftoolsuite.cfapp.config.GitSettings; import org.cftoolsuite.cfapp.domain.ApplicationOperation; +import org.cftoolsuite.cfapp.domain.ApplicationPolicy; +import org.cftoolsuite.cfapp.domain.EndpointPolicy; +import org.cftoolsuite.cfapp.domain.HygienePolicy; +import org.cftoolsuite.cfapp.domain.LegacyPolicy; import org.cftoolsuite.cfapp.domain.Policies; +import org.cftoolsuite.cfapp.domain.QueryPolicy; +import org.cftoolsuite.cfapp.domain.ResourceNotificationPolicy; import org.cftoolsuite.cfapp.domain.ServiceInstanceOperation; +import org.cftoolsuite.cfapp.domain.ServiceInstancePolicy; import org.cftoolsuite.cfapp.repository.R2dbcPoliciesRepository; +import org.cftoolsuite.cfapp.task.EndpointPolicyExecutorTask; +import org.cftoolsuite.cfapp.task.HygienePolicyExecutorTask; +import org.cftoolsuite.cfapp.task.LegacyWorkloadReportingTask; +import org.cftoolsuite.cfapp.task.PolicyExecutorTask; +import org.cftoolsuite.cfapp.task.QueryPolicyExecutorTask; +import org.cftoolsuite.cfapp.task.ResourceNotificationPolicyExecutorTask; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Service @@ -175,4 +192,40 @@ public Mono save(Policies entity) { return repo.save(entity); } + @Override + public Mono>> getTaskMap() { + Mono policiesMono = repo.findAll(); + Flux>> mapsFlux = Flux.merge( + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getApplicationPolicies())) + .collectMap(ApplicationPolicy::getId, ap -> ApplicationOperation.getTaskType(ap.getOperation())), + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getEndpointPolicies())) + .collectMap(EndpointPolicy::getId, ep -> EndpointPolicyExecutorTask.class), + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getHygienePolicies())) + .collectMap(HygienePolicy::getId, hp -> HygienePolicyExecutorTask.class), + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getLegacyPolicies())) + .collectMap(LegacyPolicy::getId, lp -> LegacyWorkloadReportingTask.class), + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getQueryPolicies())) + .collectMap(QueryPolicy::getId, qp -> QueryPolicyExecutorTask.class), + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getResourceNotificationPolicies())) + .collectMap(ResourceNotificationPolicy::getId, rnp -> ResourceNotificationPolicyExecutorTask.class), + policiesMono + .flatMapMany(p -> Flux.fromIterable(p.getServiceInstancePolicies())) + .collectMap(ServiceInstancePolicy::getId, sip -> ServiceInstanceOperation.getTaskType(sip.getOperation())) + ); + return + mapsFlux + .reduce( + new HashMap>(), (acc, map) -> { + acc.putAll(map); + return acc; + } + ); + } + } diff --git a/src/main/java/org/cftoolsuite/cfapp/task/DeleteAppPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/DeleteAppPolicyExecutorTask.java index 0a8accab..7195514a 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/DeleteAppPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/DeleteAppPolicyExecutorTask.java @@ -16,7 +16,6 @@ import org.cloudfoundry.operations.services.DeleteServiceInstanceRequest; import org.cloudfoundry.operations.services.UnbindServiceInstanceRequest; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @@ -81,13 +80,14 @@ protected Mono deleteApplication(AppDetail detail) { ); } - protected Flux deleteApplicationsWithNoServiceBindings() { + protected Flux deleteApplicationsWithNoServiceBindings(String id) { // these are the applications with no service bindings // we can delete each one without having to first unbind it from one or more service instances return policiesService .findByApplicationOperation(ApplicationOperation.DELETE) .flux() .flatMap(p -> Flux.fromIterable(p.getApplicationPolicies())) + .filter(ap -> ap.getId().equals(id)) .flatMap(ap -> appInfoService.findByApplicationPolicy(ap, false)) .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) .filter(bl -> filter.isBlacklisted(bl.getT1().getOrganization(), bl.getT1().getSpace())) @@ -95,7 +95,7 @@ protected Flux deleteApplicationsWithNoServiceBindings() { .flatMap(historicalRecordService::save); } - protected Flux deleteApplicationsWithServiceBindingsAndDeleteBoundServiceInstances() { + protected Flux deleteApplicationsWithServiceBindingsAndDeleteBoundServiceInstances(String id) { // these are the applications with service bindings // in this case the application policy has been configured with delete-services = true // so we: a) unbind one or more service instances from each application, b) delete each application, @@ -104,6 +104,7 @@ protected Flux deleteApplicationsWithServiceBindingsAndDeleteB .findByApplicationOperation(ApplicationOperation.DELETE) .flux() .flatMap(p -> Flux.fromIterable(p.getApplicationPolicies())) + .filter(ap -> ap.getId().equals(id)) .filter(f -> f.getOption("delete-services", Boolean.class) == true) .flatMap(ap -> appInfoService.findByApplicationPolicy(ap, true)) .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) @@ -120,7 +121,7 @@ protected Flux deleteApplicationsWithServiceBindingsAndDeleteB .flatMap(historicalRecordService::save); } - protected Flux deleteApplicationsWithServiceBindingsButDoNotDeleteBoundServiceInstances() { + protected Flux deleteApplicationsWithServiceBindingsButDoNotDeleteBoundServiceInstances(String id) { // these are the applications with service bindings // in this case the application policy has been configured with delete-services = false // so we: a) unbind one or more service instances from each application, b) delete each application @@ -128,6 +129,7 @@ protected Flux deleteApplicationsWithServiceBindingsButDoNotDe .findByApplicationOperation(ApplicationOperation.DELETE) .flux() .flatMap(p -> Flux.fromIterable(p.getApplicationPolicies())) + .filter(ap -> ap.getId().equals(id)) .filter(f -> f.getOption("delete-services", Boolean.class) == false) .flatMap(ap -> appInfoService.findByApplicationPolicy(ap, true)) .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) @@ -167,30 +169,25 @@ protected Mono deleteServiceInstance(AppRelationship relations } @Override - public void execute() { - log.info("DeleteAppPolicyExecutorTask started"); + public void execute(String id) { + log.info("DeleteAppPolicyExecutorTask with id={} started", id); Flux.concat( - deleteApplicationsWithNoServiceBindings(), - deleteApplicationsWithServiceBindingsButDoNotDeleteBoundServiceInstances(), - deleteApplicationsWithServiceBindingsAndDeleteBoundServiceInstances() + deleteApplicationsWithNoServiceBindings(id), + deleteApplicationsWithServiceBindingsButDoNotDeleteBoundServiceInstances(id), + deleteApplicationsWithServiceBindingsAndDeleteBoundServiceInstances(id) ) .collectList() .subscribe( result -> { - log.info("DeleteAppPolicyExecutorTask completed"); + log.info("DeleteAppPolicyExecutorTask with id={} completed", id); log.info("-- {} applications deleted.", result.size()); }, error -> { - log.error("DeleteAppPolicyExecutorTask terminated with error", error); + log.error(String.format("DeleteAppPolicyExecutorTask with id=%s terminated with error", id), error); } ); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - private String serviceInstanceName(AppRelationship relationship) { return String.join("__", relationship.getServiceName(), relationship.getServiceType(), relationship.getServicePlan()); } diff --git a/src/main/java/org/cftoolsuite/cfapp/task/DeleteServiceInstancePolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/DeleteServiceInstancePolicyExecutorTask.java index 29d3ff26..751f101f 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/DeleteServiceInstancePolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/DeleteServiceInstancePolicyExecutorTask.java @@ -12,7 +12,6 @@ import org.cloudfoundry.operations.DefaultCloudFoundryOperations; import org.cloudfoundry.operations.services.DeleteServiceInstanceRequest; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @@ -69,12 +68,13 @@ protected Mono deleteServiceInstance(ServiceInstanceDetail sd) } @Override - public void execute() { - log.info("DeleteServiceInstancePolicyExecutorTask started"); + public void execute(String id) { + log.info("DeleteServiceInstancePolicyExecutorTask with id={} started", id); policiesService .findByServiceInstanceOperation(ServiceInstanceOperation.DELETE) .flux() .flatMap(p -> Flux.fromIterable(p.getServiceInstancePolicies())) + .filter(sp -> sp.getId().equals(id)) .flatMap(sp -> serviceInfoService.findByServiceInstancePolicy(sp)) .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) .filter(bl -> filter.isBlacklisted(bl.getT1().getOrganization(), bl.getT1().getSpace())) @@ -83,17 +83,13 @@ public void execute() { .collectList() .subscribe( result -> { - log.info("DeleteServiceInstancePolicyExecutorTask completed"); + log.info("DeleteServiceInstancePolicyExecutorTask with id={} completed", id); log.info("-- {} service instances deleted.", result.size()); }, error -> { - log.error("DeleteServiceInstancePolicyExecutorTask terminated with error", error); + log.error(String.format("DeleteServiceInstancePolicyExecutorTask with id=%s terminated with error", id), error); } ); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } } diff --git a/src/main/java/org/cftoolsuite/cfapp/task/EndpointPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/EndpointPolicyExecutorTask.java index eb5bf824..dc98de51 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/EndpointPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/EndpointPolicyExecutorTask.java @@ -15,7 +15,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; @@ -61,9 +60,9 @@ private String determineName(String endpoint) { } @Override - public void execute() { - log.info("EndpointPolicyExecutorTask started"); - fetchEndpointPolicies() + public void execute(String id) { + log.info("EndpointPolicyExecutorTask with id={} started", id); + fetchEndpointPolicy(id) .concatMap(ep -> exerciseEndpoints(ep).collectList().map(result -> Tuples.of(ep, result))) .collectList() .subscribe( @@ -80,11 +79,10 @@ public void execute() { .attachments(buildAttachments(result.getT2())) ) ); - log.info("EndpointPolicyExecutorTask completed"); - log.info("-- {} endpoint policies executed.", results.size()); + log.info("EndpointPolicyExecutorTask with id={} completed", id); }, error -> { - log.error("EndpointPolicyExecutorTask terminated with error", error); + log.error(String.format("EndpointPolicyExecutorTask with id=%s terminated with error", id), error); } ); } @@ -104,18 +102,13 @@ protected Flux>> exerciseEndpoints(Endpoin .concatMap(e -> exerciseEndpoint(e).map(result -> Tuples.of(determineName(e), result))); } - protected Flux fetchEndpointPolicies() { + protected Flux fetchEndpointPolicy(String id) { return - policiesService - .findAllEndpointPolicies() + policiesService + .findEndpointPolicyById(id) .flatMapMany(policy -> Flux.fromIterable(policy.getEndpointPolicies())); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - private static List buildAttachments(List>> tuples) { List result = new ArrayList<>(); for (Tuple2> t: tuples) { diff --git a/src/main/java/org/cftoolsuite/cfapp/task/HygienePolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/HygienePolicyExecutorTask.java index a70f37ec..9c815f31 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/HygienePolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/HygienePolicyExecutorTask.java @@ -24,7 +24,6 @@ import org.cftoolsuite.cfapp.service.UserSpacesService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @@ -62,9 +61,9 @@ public HygienePolicyExecutorTask( } @Override - public void execute() { - log.info("HygienePolicyExecutorTask started"); - fetchHygienePolicies() + public void execute(String id) { + log.info("HygienePolicyExecutorTask with id={} started", id); + fetchHygienePolicy(id) .concatMap(hp -> executeHygienePolicy(hp).map(result -> Tuples.of(hp, result))) .collectList() .subscribe( @@ -73,11 +72,10 @@ public void execute() { notifyOperator(tuple); notifyUsers(tuple); }); - log.info("HygienePolicyExecutorTask completed"); - log.info("-- {} hygiene policies executed.", results.size()); + log.info("HygienePolicyExecutorTask with id={} completed", id); }, error -> { - log.error("HygienePolicyExecutorTask terminated with error", error); + log.error(String.format("HygienePolicyExecutorTask with id=%s terminated with error", id), error); } ); } @@ -91,10 +89,10 @@ protected Mono executeHygienePolicy(HygienePolicy policy) { .map(list -> builder.serviceInstances(list).build()); } - protected Flux fetchHygienePolicies() { + protected Flux fetchHygienePolicy(String id) { return policiesService - .findAllHygienePolicies() + .findHygienePolicyById(id) .flatMapMany(policy -> Flux.fromIterable(policy.getHygienePolicies())); } @@ -144,11 +142,6 @@ private void notifyUsers(Tuple2 tuple) { } } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - private static List buildAttachments(Tuple2 tuple) { String cr = System.getProperty("line.separator"); List result = new ArrayList<>(); diff --git a/src/main/java/org/cftoolsuite/cfapp/task/LegacyWorkloadReportingTask.java b/src/main/java/org/cftoolsuite/cfapp/task/LegacyWorkloadReportingTask.java index f833f1cb..965bf85d 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/LegacyWorkloadReportingTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/LegacyWorkloadReportingTask.java @@ -24,7 +24,6 @@ import org.cftoolsuite.cfapp.service.UserSpacesService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @@ -62,9 +61,9 @@ public LegacyWorkloadReportingTask( } @Override - public void execute() { - log.info("LegacyWorkloadReportingTask started"); - fetchLegacyPolicies() + public void execute(String id) { + log.info("LegacyWorkloadReportingTask with id={} started", id); + fetchLegacyPolicy(id) .concatMap(hp -> executePolicy(hp).map(result -> Tuples.of(hp, result))) .collectList() .subscribe( @@ -73,11 +72,10 @@ public void execute() { notifyOperator(tuple); notifyUsers(tuple); }); - log.info("LegacyWorkloadReportingTask completed"); - log.info("-- {} legacy workload policies executed.", results.size()); + log.info("LegacyWorkloadReportingTask with id={} completed", id); }, error -> { - log.error("LegacyWorkloadReportingTask terminated with error", error); + log.error(String.format("LegacyWorkloadReportingTask with id=%s terminated with error", id), error); } ); } @@ -91,10 +89,10 @@ protected Mono executePolicy(LegacyPolicy policy) { .map(list -> builder.appRelationships(list).build()); } - protected Flux fetchLegacyPolicies() { + protected Flux fetchLegacyPolicy(String id) { return policiesService - .findAllLegacyPolicies() + .findLegacyPolicyById(id) .flatMapMany(policy -> Flux.fromIterable(policy.getLegacyPolicies())); } @@ -144,11 +142,6 @@ private void notifyUsers(Tuple2 tuple) { } } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - private static List buildAttachments(Tuple2 tuple) { String cr = System.getProperty("line.separator"); List result = new ArrayList<>(); diff --git a/src/main/java/org/cftoolsuite/cfapp/task/PoliciesLoader.java b/src/main/java/org/cftoolsuite/cfapp/task/PoliciesLoader.java index 2a40b2a5..745f8552 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/PoliciesLoader.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/PoliciesLoader.java @@ -15,11 +15,13 @@ import org.cftoolsuite.cfapp.domain.QueryPolicy; import org.cftoolsuite.cfapp.domain.ResourceNotificationPolicy; import org.cftoolsuite.cfapp.domain.ServiceInstancePolicy; +import org.cftoolsuite.cfapp.event.PoliciesLoadedEvent; import org.cftoolsuite.cfapp.event.StacksRetrievedEvent; import org.cftoolsuite.cfapp.service.PoliciesService; import org.eclipse.jgit.lib.Repository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -47,20 +49,23 @@ public class PoliciesLoader implements ApplicationListener private final GitSettings settings; private final PoliciesValidator validator; private final ObjectMapper mapper; + private ApplicationEventPublisher publisher; @Autowired public PoliciesLoader( - GitClient client, - PoliciesService service, - GitSettings settings, - PoliciesValidator validator, - ObjectMapper mapper - ) { + GitClient client, + PoliciesService service, + GitSettings settings, + PoliciesValidator validator, + ObjectMapper mapper, + ApplicationEventPublisher publisher + ) { this.client = client; this.service = service; this.settings = settings; this.validator = validator; this.mapper = mapper; + this.publisher = publisher; } public void load() { @@ -143,8 +148,10 @@ public void load() { .legacyPolicies(legacyPolicies) .build() )) + .then(service.findAll()) .subscribe( result -> { + publisher.publishEvent(new PoliciesLoadedEvent(this).policies(result)); log.info("PoliciesLoader completed"); log.info( String.format("-- Loaded %d application policies, %d service instance policies, %d endpoint policies, %d query policies, %d hygiene policies, %d resource notification policies, and %d legacy policies.", diff --git a/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTask.java index c80d0a50..11fef01c 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTask.java @@ -1,5 +1,5 @@ package org.cftoolsuite.cfapp.task; public interface PolicyExecutorTask { - void execute(); + void execute(String policyId); } diff --git a/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTaskScheduler.java b/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTaskScheduler.java new file mode 100644 index 00000000..804ede05 --- /dev/null +++ b/src/main/java/org/cftoolsuite/cfapp/task/PolicyExecutorTaskScheduler.java @@ -0,0 +1,73 @@ +package org.cftoolsuite.cfapp.task; + +import org.cftoolsuite.cfapp.domain.Policies; +import org.cftoolsuite.cfapp.domain.Policy; +import org.cftoolsuite.cfapp.event.PoliciesLoadedEvent; +import org.cftoolsuite.cfapp.service.PoliciesService; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Service; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Slf4j +@Service +@EnableScheduling +public class PolicyExecutorTaskScheduler implements ApplicationListener { + + private final BeanFactory factory; + private final PoliciesService service; + private final TaskScheduler scheduler; + + @Autowired + public PolicyExecutorTaskScheduler( + BeanFactory factory, + PoliciesService service, + TaskScheduler scheduler) { + this.factory = factory; + this.service = service; + this.scheduler = scheduler; + } + + public void scheduleTasks(Policies policies) { + log.info("PolicyExecutorTaskScheduler started"); + service + .getTaskMap() + .flatMapMany(taskTypeMap -> + Flux.fromIterable(taskTypeMap.entrySet()) + .flatMap(entry -> { + String policyId = entry.getKey(); + Class taskClass = entry.getValue(); + PolicyExecutorTask task = factory.getBean(taskClass); + Policy policy = policies.getById(policyId); + return + Mono + .fromRunnable(() -> + scheduler.schedule(() -> + task.execute(policyId), new CronTrigger(policy.getCronExpression()) + ) + ); + })) + .count() + .subscribe( + result -> { + log.info("PolicyExecutorTaskScheduler completed. {} policies scheduled.", result); + }, + error -> { + log.error("PolicyExecutorTaskScheduler terminated with error", error); + } + ); + } + + @Override + public void onApplicationEvent(PoliciesLoadedEvent event) { + scheduleTasks(event.getPolicies()); + } + +} diff --git a/src/main/java/org/cftoolsuite/cfapp/task/QueryPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/QueryPolicyExecutorTask.java index 3ce780da..04bd5c96 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/QueryPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/QueryPolicyExecutorTask.java @@ -16,7 +16,6 @@ import org.cftoolsuite.cfapp.service.QueryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import io.r2dbc.spi.Row; @@ -50,9 +49,9 @@ public QueryPolicyExecutorTask( } @Override - public void execute() { - log.info("QueryPolicyExecutorTask started"); - fetchQueryPolicies() + public void execute(String id) { + log.info("QueryPolicyExecutorTask with id={} started", id); + fetchQueryPolicy(id) .concatMap(qp -> executeQueries(qp).collectList().map(result -> Tuples.of(qp, result))) .collectList() .subscribe( @@ -69,11 +68,10 @@ public void execute() { .attachments(buildAttachments(result.getT2())) ) ); - log.info("QueryPolicyExecutorTask completed"); - log.info("-- {} query policies executed.", results.size()); + log.info("QueryPolicyExecutorTask with id={} completed", id); }, error -> { - log.error("QueryPolicyExecutorTask terminated with error", error); + log.error(String.format("QueryPolicyExecutorTask with id=%s terminated with error", id), error); } ); } @@ -86,7 +84,7 @@ protected Flux> executeQueries(QueryPolicy policy) { protected Mono executeQuery(Query query) { Flux> results = - queryService + queryService .executeQuery(query); return results .flatMap(QueryPolicyExecutorTask::toCommaSeparatedValue) @@ -94,18 +92,13 @@ protected Mono executeQuery(Query query) { .flatMap(QueryPolicyExecutorTask::constructCsvOutput); } - protected Flux fetchQueryPolicies() { + protected Flux fetchQueryPolicy(String id) { return - policiesService - .findAllQueryPolicies() + policiesService + .findQueryPolicyById(id) .flatMapMany(policy -> Flux.fromIterable(policy.getQueryPolicies())); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - private static List buildAttachments(List> tuples) { List result = new ArrayList<>(); for (Tuple2 t: tuples) { diff --git a/src/main/java/org/cftoolsuite/cfapp/task/ResourceNotificationPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/ResourceNotificationPolicyExecutorTask.java index 7e7b422e..6f8fbd9a 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/ResourceNotificationPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/ResourceNotificationPolicyExecutorTask.java @@ -12,7 +12,6 @@ import org.cftoolsuite.cfapp.service.ResourceMetadataService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -46,9 +45,9 @@ public ResourceNotificationPolicyExecutorTask( } @Override - public void execute() { - log.info("ResourceNotificationPolicyExecutorTask started"); - fetchResourceNotificationPolicies() + public void execute(String id) { + log.info("ResourceNotificationPolicyExecutorTask with id={} started", id); + fetchResourceNotificationPolicy(id) .collectList() .subscribe( results -> { @@ -57,19 +56,18 @@ public void execute() { notifyOwners(mp,label); }); }); - log.info("ResourceNotificationPolicyExecutorTask completed"); - log.info("-- {} resource notification policies executed.", results.size()); + log.info("ResourceNotificationPolicyExecutorTask with id={} completed", id); }, error -> { - log.error("ResourceNotificationPolicyExecutorTask terminated with error", error); + log.error(String.format("ResourceNotificationPolicyExecutorTask with id=%s terminated with error", id), error); } ); } - protected Flux fetchResourceNotificationPolicies() { + protected Flux fetchResourceNotificationPolicy(String id) { return policiesService - .findAllResourceNotificationPolicies() + .findResourceNotificationPolicyById(id) .flatMapMany(policy -> Flux.fromIterable(policy.getResourceNotificationPolicies())); } @@ -121,9 +119,4 @@ private boolean isWhitelisted(ResourceNotificationPolicy policy, String resource whitelist.isEmpty() ? true: policy.getResourceWhiteList().contains(resource); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - } \ No newline at end of file diff --git a/src/main/java/org/cftoolsuite/cfapp/task/ScaleAppInstancesPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/ScaleAppInstancesPolicyExecutorTask.java index d4fa269c..884a25b8 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/ScaleAppInstancesPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/ScaleAppInstancesPolicyExecutorTask.java @@ -14,7 +14,6 @@ import org.cloudfoundry.operations.DefaultCloudFoundryOperations; import org.cloudfoundry.operations.applications.ScaleApplicationRequest; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @@ -47,25 +46,20 @@ public ScaleAppInstancesPolicyExecutorTask( } @Override - public void execute() { - log.info("ScaleAppInstancesPolicyExecutorTask started"); - scaleApplications() + public void execute(String id) { + log.info("ScaleAppInstancesPolicyExecutorTask with id={} started", id); + scaleApplications(id) .subscribe( result -> { - log.info("ScaleAppInstancesPolicyExecutorTask completed"); + log.info("ScaleAppInstancesPolicyExecutorTask with id={} completed", id); log.info("-- {} applications scaled.", result.size()); }, error -> { - log.error("ScaleAppInstancesPolicyExecutorTask terminated with error", error); + log.error(String.format("ScaleAppInstancesPolicyExecutorTask with id=%s terminated with error", id), error); } ); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - protected Mono scaleApplication(ApplicationPolicy policy, AppDetail detail) { return DefaultCloudFoundryOperations.builder() .from(opsClient) @@ -96,12 +90,13 @@ protected Mono scaleApplication(ApplicationPolicy policy, AppD ); } - protected Mono> scaleApplications() { + protected Mono> scaleApplications(String id) { return policiesService .findByApplicationOperation(ApplicationOperation.SCALE_INSTANCES) .flux() .flatMap(p -> Flux.fromIterable(p.getApplicationPolicies())) + .filter(ap -> ap.getId().equals(id)) .flatMap(ap -> Flux.concat(appInfoService.findByApplicationPolicy(ap, false), appInfoService.findByApplicationPolicy(ap, true))) .distinct() .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) diff --git a/src/main/java/org/cftoolsuite/cfapp/task/StackChangeAppInstancesPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/StackChangeAppInstancesPolicyExecutorTask.java index f1cda159..175b0bec 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/StackChangeAppInstancesPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/StackChangeAppInstancesPolicyExecutorTask.java @@ -36,7 +36,6 @@ import org.cloudfoundry.util.DelayUtils; import org.cloudfoundry.util.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.Builder; @@ -112,16 +111,16 @@ private Mono createBuild(PackageResource packageResource, A } @Override - public void execute() { - log.info("StackChangeAppInstancesPolicyExecutorTask started"); - stackChangeApplications() + public void execute(String id) { + log.info("StackChangeAppInstancesPolicyExecutorTask with id={} started", id); + stackChangeApplications(id) .subscribe( result -> { - log.info("StackChangeAppInstancesPolicyExecutorTask completed"); + log.info("StackChangeAppInstancesPolicyExecutorTask with id={} completed", id); log.info("-- {} applications updated.", result.size()); }, error -> { - log.error("StackChangeAppInstancesPolicyExecutorTask terminated with error", error); + log.error(String.format("StackChangeAppInstancesPolicyExecutorTask with id=%s terminated with error", id), error); } ); } @@ -162,11 +161,6 @@ private Mono restartApp(AppDetail detail) { .restart(RestartApplicationRequest.builder().name(detail.getAppName()).build()); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - private Mono setDroplet(GetBuildResponse build, AppDetail detail) { Relationship data = Relationship.builder().id(build.getDroplet().getId()).build(); return DefaultCloudFoundryOperations.builder() @@ -203,12 +197,13 @@ protected Mono stackChangeApplication(ApplicationPolicy policy } // FIXME current implementation has no recoverability and is not capable of zero-downtime deployment - protected Mono> stackChangeApplications() { + protected Mono> stackChangeApplications(String id) { return policiesService .findByApplicationOperation(ApplicationOperation.CHANGE_STACK) .flux() .flatMap(p -> Flux.fromIterable(p.getApplicationPolicies())) + .filter(ap -> ap.getId().equals(id)) .flatMap(ap -> Flux.concat(appInfoService.findByApplicationPolicy(ap, false), appInfoService.findByApplicationPolicy(ap, true))) .distinct() .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) diff --git a/src/main/java/org/cftoolsuite/cfapp/task/StopAppPolicyExecutorTask.java b/src/main/java/org/cftoolsuite/cfapp/task/StopAppPolicyExecutorTask.java index 44e82e82..40ef880b 100644 --- a/src/main/java/org/cftoolsuite/cfapp/task/StopAppPolicyExecutorTask.java +++ b/src/main/java/org/cftoolsuite/cfapp/task/StopAppPolicyExecutorTask.java @@ -12,7 +12,6 @@ import org.cloudfoundry.client.v3.applications.StopApplicationRequest; import org.cloudfoundry.operations.DefaultCloudFoundryOperations; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @@ -45,26 +44,21 @@ public StopAppPolicyExecutorTask( } @Override - public void execute() { - log.info("StopAppPolicyExecutorTask started"); - stopApplications() + public void execute(String id) { + log.info("StopAppPolicyExecutorTask with id={} started", id); + stopApplications(id) .collectList() .subscribe( result -> { - log.info("StopAppPolicyExecutorTask completed"); + log.info("StopAppPolicyExecutorTask with id={} completed", id); log.info("-- {} applications stopped.", result.size()); }, error -> { - log.error("StopAppPolicyExecutorTask terminated with error", error); + log.error(String.format("StopAppPolicyExecutorTask with id=%s terminated with error", id), error); } ); } - @Scheduled(cron = "${cron.execution}") - protected void runTask() { - execute(); - } - protected Mono stopApplication(AppDetail detail) { return DefaultCloudFoundryOperations.builder() .from(opsClient) @@ -95,11 +89,12 @@ protected Mono stopApplication(AppDetail detail) { ); } - protected Flux stopApplications() { + protected Flux stopApplications(String id) { return policiesService .findByApplicationOperation(ApplicationOperation.STOP) .flux() .flatMap(p -> Flux.fromIterable(p.getApplicationPolicies())) + .filter(ap -> ap.equals(id)) .flatMap(ap -> Flux.concat(appInfoService.findByApplicationPolicy(ap, false), appInfoService.findByApplicationPolicy(ap, true))) .distinct() .filter(wl -> filter.isWhitelisted(wl.getT2(), wl.getT1().getOrganization())) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a1c20146..834287be 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -63,8 +63,6 @@ cf: cron: # the top of every hour daily collection: "0 0 * * * *" - # at 5:30am on Mondays weekly - execution: "0 30 5 * * MON" logging: level: @@ -117,6 +115,10 @@ spring: sql: init: enabled: false + task: + scheduling: + pool: + size: 5 # Java Mail # Reset the values for spring.mail.username, spring.mail.password diff --git a/src/main/resources/db/h2/schema.ddl b/src/main/resources/db/h2/schema.ddl index e79a58c4..f31703c3 100644 --- a/src/main/resources/db/h2/schema.ddl +++ b/src/main/resources/db/h2/schema.ddl @@ -3,15 +3,15 @@ CREATE TABLE IF NOT EXISTS spaces ( org_id VARCHAR(50) NOT NULL, space_id VARCHA CREATE TABLE IF NOT EXISTS application_detail ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(500), buildpack VARCHAR(500), buildpack_version VARCHAR(50), image VARCHAR(250), stack VARCHAR(25), running_instances INT, total_instances INT, memory_used BIGINT, disk_used BIGINT, memory_quota BIGINT, disk_quota BIGINT, urls VARCHAR(512000), last_pushed TIMESTAMP, last_event VARCHAR(50), last_event_actor VARCHAR(100), last_event_time TIMESTAMP, requested_state VARCHAR(25), buildpack_release_type VARCHAR(100), buildpack_release_date TIMESTAMP, buildpack_latest_version VARCHAR(50), buildpack_latest_url VARCHAR(500)); CREATE TABLE IF NOT EXISTS java_application_detail ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(500), droplet_id VARCHAR(100), pom_contents VARCHAR(512000), jars VARCHAR(512000), spring_dependencies VARCHAR(2000) ); CREATE TABLE IF NOT EXISTS service_instance_detail ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), service_instance_id VARCHAR(50), service_name VARCHAR(100), service VARCHAR(100), description VARCHAR(1000), plan VARCHAR(50), type VARCHAR(30), bound_applications VARCHAR(512000), last_operation VARCHAR(50), last_updated TIMESTAMP, dashboard_url VARCHAR(250), requested_state VARCHAR(25) ); -CREATE TABLE IF NOT EXISTS application_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), operation VARCHAR(25), description VARCHAR(1000), state VARCHAR(25), options VARCHAR(512000), organization_whitelist VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS service_instance_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), operation VARCHAR(25), description VARCHAR(1000), options VARCHAR(512000), organization_whitelist VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS endpoint_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), description VARCHAR(1000), endpoints VARCHAR(512000), email_notification_template VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS query_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), description VARCHAR(1000), queries VARCHAR(512000), email_notification_template VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS hygiene_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), days_since_last_update INTEGER, operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000), include_applications BOOLEAN, include_service_instances BOOLEAN ) -CREATE TABLE IF NOT EXISTS legacy_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), stacks VARCHAR(250), service_offerings VARCHAR(100), operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000) ) +CREATE TABLE IF NOT EXISTS application_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), operation VARCHAR(25), description VARCHAR(1000), state VARCHAR(25), options VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS service_instance_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), operation VARCHAR(25), description VARCHAR(1000), options VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS endpoint_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), description VARCHAR(1000), endpoints VARCHAR(512000), email_notification_template VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS query_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), description VARCHAR(1000), queries VARCHAR(512000), email_notification_template VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS hygiene_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), days_since_last_update INTEGER, operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20), include_applications BOOLEAN, include_service_instances BOOLEAN ) +CREATE TABLE IF NOT EXISTS legacy_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), stacks VARCHAR(250), service_offerings VARCHAR(100), operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20) ) CREATE TABLE IF NOT EXISTS application_relationship ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(100), service_instance_id VARCHAR(50), service_name VARCHAR(100), service_offering VARCHAR(100), service_plan VARCHAR(50), service_type VARCHAR(30) ); CREATE TABLE IF NOT EXISTS historical_record ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, transaction_date_time TIMESTAMP, action_taken VARCHAR(20), organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), service_instance_id VARCHAR(50), type VARCHAR(20), name VARCHAR(300) ); -CREATE TABLE IF NOT EXISTS resource_notification_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), resource_email_template VARCHAR(512000), resource_email_metadata VARCHAR(512000), resource_whitelist VARCHAR(512000), resource_blacklist VARCHAR(512000) ); +CREATE TABLE IF NOT EXISTS resource_notification_policy ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), resource_email_template VARCHAR(512000), resource_email_metadata VARCHAR(512000), resource_whitelist VARCHAR(512000), resource_blacklist VARCHAR(512000), cron_expression VARCHAR(20) ); CREATE TABLE IF NOT EXISTS space_users ( pk BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, organization varchar(100), space varchar(100), auditors VARCHAR(512000), managers VARCHAR(512000), developers VARCHAR(512000) ); CREATE TABLE IF NOT EXISTS time_keeper ( collection_time TIMESTAMP(9) PRIMARY KEY ); CREATE VIEW service_bindings AS select ad.pk, ad.organization, ad.space, ad.app_id, ar.service_instance_id, ad.app_name, ad.buildpack, ad.buildpack_version, ad.image, ad.stack, ad.running_instances, ad.total_instances, ad.memory_used, ad.disk_used, ad.urls, ad.last_pushed, ad.last_event, ad.last_event_actor, ad.last_event_time, ad.requested_state, ad.buildpack_release_type, ad.buildpack_release_date, ad.buildpack_latest_version, ad.buildpack_latest_url from application_detail ad left join application_relationship ar on ad.app_id = ar.app_id; diff --git a/src/main/resources/db/mysql/schema.ddl b/src/main/resources/db/mysql/schema.ddl index 1113cc19..2d74fa60 100644 --- a/src/main/resources/db/mysql/schema.ddl +++ b/src/main/resources/db/mysql/schema.ddl @@ -3,15 +3,15 @@ CREATE TABLE IF NOT EXISTS spaces ( org_id VARCHAR(50) NOT NULL, space_id VARCHA CREATE TABLE IF NOT EXISTS application_detail ( pk BIGINT NOT NULL AUTO_INCREMENT, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(100), buildpack VARCHAR(500), buildpack_version VARCHAR(50), image VARCHAR(250), stack VARCHAR(25), running_instances INT, total_instances INT, memory_used BIGINT UNSIGNED, disk_used BIGINT UNSIGNED, memory_quota BIGINT UNSIGNED, disk_quota BIGINT UNSIGNED, urls VARCHAR(2000), last_pushed DATETIME, last_event VARCHAR(50), last_event_actor VARCHAR(100), last_event_time DATETIME, requested_state VARCHAR(25), buildpack_release_type VARCHAR(100), buildpack_release_date DATETIME, buildpack_latest_version VARCHAR(50), buildpack_latest_url VARCHAR(500), PRIMARY KEY(pk) ); CREATE TABLE IF NOT EXISTS java_application_detail ( pk BIGINT NOT NULL AUTO_INCREMENT, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(500), droplet_id VARCHAR(100), pom_contents TEXT, jars TEXT, spring_dependencies TEXT, PRIMARY KEY(pk) ); CREATE TABLE IF NOT EXISTS service_instance_detail ( pk BIGINT NOT NULL AUTO_INCREMENT, organization VARCHAR(100), space VARCHAR(100), service_instance_id VARCHAR(50), service_name VARCHAR(100), service VARCHAR(100), description VARCHAR(1000), plan VARCHAR(50), type VARCHAR(30), bound_applications VARCHAR(2000), last_operation VARCHAR(50), last_updated DATETIME, dashboard_url VARCHAR(250), requested_state VARCHAR(25), PRIMARY KEY(pk) ); -CREATE TABLE IF NOT EXISTS application_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), operation VARCHAR(25), description VARCHAR(1000), state VARCHAR(25), options VARCHAR(2000), organization_whitelist VARCHAR(2000), PRIMARY KEY(pk) ); -CREATE TABLE IF NOT EXISTS service_instance_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), operation VARCHAR(25), description VARCHAR(1000), options VARCHAR(2000), organization_whitelist VARCHAR(2000), PRIMARY KEY(pk) ); -CREATE TABLE IF NOT EXISTS endpoint_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), description VARCHAR(1000), endpoints VARCHAR(2000), email_notification_template VARCHAR(2000), PRIMARY KEY(pk) ); -CREATE TABLE IF NOT EXISTS query_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), description VARCHAR(1000), queries VARCHAR(2000), email_notification_template VARCHAR(2000), PRIMARY KEY(pk) ); -CREATE TABLE IF NOT EXISTS hygiene_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), days_since_last_update INTEGER, operator_email_template VARCHAR(2000), notifyee_email_template VARCHAR(2000), organization_whitelist VARCHAR(2000), include_applications BOOLEAN, include_service_instances BOOLEAN, PRIMARY KEY(pk) ) -CREATE TABLE IF NOT EXISTS legacy_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), stacks VARCHAR(250), service_offerings VARCHAR(100), operator_email_template VARCHAR(2000), notifyee_email_template VARCHAR(2000), organization_whitelist VARCHAR(2000), PRIMARY KEY(pk) ) +CREATE TABLE IF NOT EXISTS application_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), operation VARCHAR(25), description VARCHAR(1000), state VARCHAR(25), options VARCHAR(2000), organization_whitelist VARCHAR(2000), cron_expression VARCHAR(20), PRIMARY KEY(pk) ); +CREATE TABLE IF NOT EXISTS service_instance_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), operation VARCHAR(25), description VARCHAR(1000), options VARCHAR(2000), organization_whitelist VARCHAR(2000), cron_expression VARCHAR(20), PRIMARY KEY(pk) ); +CREATE TABLE IF NOT EXISTS endpoint_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), description VARCHAR(1000), endpoints VARCHAR(2000), email_notification_template VARCHAR(2000), cron_expression VARCHAR(20), PRIMARY KEY(pk) ); +CREATE TABLE IF NOT EXISTS query_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), description VARCHAR(1000), queries VARCHAR(2000), email_notification_template VARCHAR(2000), cron_expression VARCHAR(20), PRIMARY KEY(pk) ); +CREATE TABLE IF NOT EXISTS hygiene_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), days_since_last_update INTEGER, operator_email_template VARCHAR(2000), notifyee_email_template VARCHAR(2000), organization_whitelist VARCHAR(2000), cron_expression VARCHAR(20), include_applications BOOLEAN, include_service_instances BOOLEAN, PRIMARY KEY(pk) ) +CREATE TABLE IF NOT EXISTS legacy_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), stacks VARCHAR(250), service_offerings VARCHAR(100), operator_email_template VARCHAR(2000), notifyee_email_template VARCHAR(2000), organization_whitelist VARCHAR(2000), cron_expression VARCHAR(20), PRIMARY KEY(pk) ) CREATE TABLE IF NOT EXISTS application_relationship ( pk BIGINT NOT NULL AUTO_INCREMENT, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(100), service_instance_id VARCHAR(50), service_name VARCHAR(100), service_offering VARCHAR(100), service_plan VARCHAR(50), service_type VARCHAR(30), PRIMARY KEY(pk) ); CREATE TABLE IF NOT EXISTS historical_record ( pk BIGINT NOT NULL AUTO_INCREMENT, transaction_date_time DATETIME, action_taken VARCHAR(20), organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), service_instance_id VARCHAR(50), type VARCHAR(20), name VARCHAR(300), PRIMARY KEY(pk) ); -CREATE TABLE IF NOT EXISTS resource_notification_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), resource_email_template VARCHAR(2000), resource_email_metadata VARCHAR(2000), resource_whitelist VARCHAR(2000), resource_blacklist VARCHAR(2000), PRIMARY KEY(pk) ); +CREATE TABLE IF NOT EXISTS resource_notification_policy ( pk BIGINT NOT NULL AUTO_INCREMENT, id VARCHAR(50), git_commit VARCHAR(40), resource_email_template VARCHAR(2000), resource_email_metadata VARCHAR(2000), resource_whitelist VARCHAR(2000), resource_blacklist VARCHAR(2000), PRIMARY KEY(pk), cron_expression VARCHAR(20) ); CREATE TABLE IF NOT EXISTS space_users ( pk BIGINT NOT NULL AUTO_INCREMENT, organization varchar(100), space varchar(100), auditors VARCHAR(2000), managers VARCHAR(2000), developers VARCHAR(2000), PRIMARY KEY(pk) ); CREATE TABLE IF NOT EXISTS time_keeper ( collection_time DATETIME, PRIMARY KEY(collection_time) ); CREATE OR REPLACE VIEW service_bindings AS select ad.pk, ad.organization, ad.space, ad.app_id, ar.service_instance_id, ad.app_name, ad.buildpack, ad.buildpack_version, ad.image, ad.stack, ad.running_instances, ad.total_instances, ad.memory_used, ad.disk_used, ad.urls, ad.last_pushed, ad.last_event, ad.last_event_actor, ad.last_event_time, ad.requested_state, ad.buildpack_release_type, ad.buildpack_release_date, ad.buildpack_latest_version, ad.buildpack_latest_url from application_detail ad left join application_relationship ar on ad.app_id = ar.app_id; diff --git a/src/main/resources/db/postgresql/schema.ddl b/src/main/resources/db/postgresql/schema.ddl index cb52d2e4..7e6faa45 100644 --- a/src/main/resources/db/postgresql/schema.ddl +++ b/src/main/resources/db/postgresql/schema.ddl @@ -3,15 +3,15 @@ CREATE TABLE IF NOT EXISTS spaces ( org_id VARCHAR(50) NOT NULL, space_id VARCHA CREATE TABLE IF NOT EXISTS application_detail ( pk BIGSERIAL PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(100), buildpack VARCHAR(500), buildpack_version VARCHAR(50), image VARCHAR(250), stack VARCHAR(25), running_instances INT, total_instances INT, memory_used BIGINT, disk_used BIGINT, memory_quota BIGINT, disk_quota BIGINT, urls VARCHAR(512000), last_pushed TIMESTAMP, last_event VARCHAR(50), last_event_actor VARCHAR(100), last_event_time TIMESTAMP, requested_state VARCHAR(25), buildpack_release_type VARCHAR(100), buildpack_release_date TIMESTAMP, buildpack_latest_version VARCHAR(50), buildpack_latest_url VARCHAR(500) ); CREATE TABLE IF NOT EXISTS java_application_detail ( pk BIGSERIAL PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(100), droplet_id VARCHAR(100), pom_contents VARCHAR(512000), jars VARCHAR(512000), spring_dependencies VARCHAR(2000) ); CREATE TABLE IF NOT EXISTS service_instance_detail ( pk BIGSERIAL PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), service_instance_id VARCHAR(50), service_name VARCHAR(100), service VARCHAR(100), description VARCHAR(1000), plan VARCHAR(50), type VARCHAR(30), bound_applications VARCHAR(512000), last_operation VARCHAR(50), last_updated TIMESTAMP, dashboard_url VARCHAR(250), requested_state VARCHAR(25) ); -CREATE TABLE IF NOT EXISTS application_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), operation VARCHAR(25), description VARCHAR(1000), state VARCHAR(25), options VARCHAR(512000), organization_whitelist VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS service_instance_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), operation VARCHAR(25), description VARCHAR(1000), options VARCHAR(512000), organization_whitelist VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS endpoint_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), description VARCHAR(1000), endpoints VARCHAR(512000), email_notification_template VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS query_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), description VARCHAR(1000), queries VARCHAR(512000), email_notification_template VARCHAR(512000) ); -CREATE TABLE IF NOT EXISTS hygiene_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), days_since_last_update INTEGER, operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000), include_applications BOOLEAN, include_service_instances BOOLEAN ) -CREATE TABLE IF NOT EXISTS legacy_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), stacks VARCHAR(250), service_offerings VARCHAR(100), operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000) ) +CREATE TABLE IF NOT EXISTS application_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), operation VARCHAR(25), description VARCHAR(1000), state VARCHAR(25), options VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS service_instance_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), operation VARCHAR(25), description VARCHAR(1000), options VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS endpoint_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), description VARCHAR(1000), endpoints VARCHAR(512000), email_notification_template VARCHAR(512000) ); +CREATE TABLE IF NOT EXISTS query_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), description VARCHAR(1000), queries VARCHAR(512000), email_notification_template VARCHAR(512000), cron_expression VARCHAR(20) ); +CREATE TABLE IF NOT EXISTS hygiene_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), days_since_last_update INTEGER, operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20), include_applications BOOLEAN, include_service_instances BOOLEAN ) +CREATE TABLE IF NOT EXISTS legacy_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), stacks VARCHAR(250), service_offerings VARCHAR(100), operator_email_template VARCHAR(512000), notifyee_email_template VARCHAR(512000), organization_whitelist VARCHAR(512000), cron_expression VARCHAR(20) ) CREATE TABLE IF NOT EXISTS application_relationship ( pk BIGSERIAL PRIMARY KEY, organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), app_name VARCHAR(100), service_instance_id VARCHAR(50), service_name VARCHAR(100), service_offering VARCHAR(100), service_plan VARCHAR(50), service_type VARCHAR(30) ); CREATE TABLE IF NOT EXISTS historical_record ( pk BIGSERIAL PRIMARY KEY, transaction_date_time TIMESTAMP, action_taken VARCHAR(20), organization VARCHAR(100), space VARCHAR(100), app_id VARCHAR(50), service_instance_id VARCHAR(50), type VARCHAR(20), name VARCHAR(300) ); -CREATE TABLE IF NOT EXISTS resource_notification_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), resource_email_template VARCHAR(512000), resource_email_metadata VARCHAR(512000), resource_whitelist VARCHAR(512000), resource_blacklist VARCHAR(512000) ); +CREATE TABLE IF NOT EXISTS resource_notification_policy ( pk BIGSERIAL PRIMARY KEY, id VARCHAR(50), git_commit VARCHAR(40), resource_email_template VARCHAR(512000), resource_email_metadata VARCHAR(512000), resource_whitelist VARCHAR(512000), resource_blacklist VARCHAR(512000), cron_expression VARCHAR(20) ); CREATE TABLE IF NOT EXISTS space_users ( pk BIGSERIAL PRIMARY KEY, organization varchar(100), space varchar(100), auditors VARCHAR(512000), managers VARCHAR(512000), developers VARCHAR(512000) ); CREATE TABLE IF NOT EXISTS time_keeper ( collection_time TIMESTAMP PRIMARY KEY ); CREATE OR REPLACE VIEW service_bindings AS select ad.pk, ad.organization, ad.space, ad.app_id, ar.service_instance_id, ad.app_name, ad.buildpack, ad.buildpack_version, ad.image, ad.stack, ad.running_instances, ad.total_instances, ad.memory_used, ad.disk_used, ad.urls, ad.last_pushed, ad.last_event, ad.last_event_actor, ad.last_event_time, ad.requested_state, ad.buildpack_release_type, ad.buildpack_release_date, ad.buildpack_latest_version, ad.buildpack_latest_url from application_detail ad left join application_relationship ar on ad.app_id = ar.app_id;