Skip to content

Commit

Permalink
refactor(cloudfoundry): move processes into their own services (#5316)
Browse files Browse the repository at this point in the history
* feat(cloudfoundry): added metric event listener for outbound requests to cf

* remove kork-web

* refactor(cloudfoundry): move processes into their own service

* change copyright to armory

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
zachsmith1 and mergify[bot] committed Mar 29, 2021
1 parent 186257e commit 65e5e52
Show file tree
Hide file tree
Showing 17 changed files with 312 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public class Applications {
private final String metricsUri;
private final ApplicationService api;
private final Spaces spaces;
private final Processes processes;
private final Integer resultsPerPage;

private final ForkJoinPool forkJoinPool;
private final LoadingCache<String, CloudFoundryServerGroup> serverGroupCache;

Expand All @@ -69,15 +69,16 @@ public Applications(
String metricsUri,
ApplicationService api,
Spaces spaces,
Processes processes,
Integer resultsPerPage,
ForkJoinPool forkJoinPool) {
this.account = account;
this.appsManagerUri = appsManagerUri;
this.metricsUri = metricsUri;
this.api = api;
this.spaces = spaces;
this.processes = processes;
this.resultsPerPage = resultsPerPage;

this.forkJoinPool = forkJoinPool;
this.serverGroupCache =
CacheBuilder.newBuilder()
Expand Down Expand Up @@ -265,7 +266,7 @@ private CloudFoundryServerGroup map(Application application) {
String appId = application.getGuid();
ApplicationEnv applicationEnv =
safelyCall(() -> api.findApplicationEnvById(appId)).orElse(null);
Process process = safelyCall(() -> api.findProcessById(appId)).orElse(null);
Process process = processes.findProcessById(appId).orElse(null);

CloudFoundryDroplet droplet = null;
try {
Expand Down Expand Up @@ -529,41 +530,6 @@ public CloudFoundryServerGroup createApplication(
"Cloud Foundry signaled that application creation succeeded but failed to provide a response."));
}

public void scaleApplication(
String guid,
@Nullable Integer instances,
@Nullable Integer memInMb,
@Nullable Integer diskInMb)
throws CloudFoundryApiException {
if ((memInMb == null && diskInMb == null && instances == null)
|| (Integer.valueOf(0).equals(memInMb)
&& Integer.valueOf(0).equals(diskInMb)
&& Integer.valueOf(0).equals(instances))) {
return;
}
safelyCall(
() -> api.scaleApplication(guid, new ScaleApplication(instances, memInMb, diskInMb)));
}

public void updateProcess(
String guid,
@Nullable String command,
@Nullable String healthCheckType,
@Nullable String healthCheckEndpoint)
throws CloudFoundryApiException {
final Process.HealthCheck healthCheck =
healthCheckType != null ? new Process.HealthCheck().setType(healthCheckType) : null;
if (healthCheckEndpoint != null && !healthCheckEndpoint.isEmpty() && healthCheck != null) {
healthCheck.setData(new Process.HealthCheckData().setEndpoint(healthCheckEndpoint));
}
if (command != null && command.isEmpty()) {
throw new IllegalArgumentException(
"Buildpack commands cannot be empty. Please specify a custom command or set it to null to use the original buildpack command.");
}

safelyCall(() -> api.updateProcess(guid, new UpdateProcess(command, healthCheck)));
}

public String createPackage(CreatePackage createPackageRequest) throws CloudFoundryApiException {
return safelyCall(() -> api.createPackage(createPackageRequest))
.map(Package::getGuid)
Expand Down Expand Up @@ -656,27 +622,6 @@ public void setCurrentDroplet(String appGuid, String dropletGuid)
() -> api.setCurrentDroplet(appGuid, new ToOneRelationship(new Relationship(dropletGuid))));
}

@Nullable
public ProcessStats.State getProcessState(String appGuid) throws CloudFoundryApiException {
return safelyCall(() -> api.findProcessStatsById(appGuid))
.map(
pr ->
pr.getResources().stream()
.findAny()
.map(ProcessStats::getState)
.orElseGet(
() ->
safelyCall(() -> api.findById(appGuid))
.filter(
application ->
CloudFoundryServerGroup.State.STARTED.equals(
CloudFoundryServerGroup.State.valueOf(
application.getState())))
.map(appState -> ProcessStats.State.RUNNING)
.orElse(ProcessStats.State.DOWN)))
.orElse(ProcessStats.State.DOWN);
}

public List<Resource<com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v2.Application>>
getTakenSlots(String clusterName, String spaceId) {
String finalName = buildFinalAsgName(clusterName);
Expand All @@ -701,4 +646,18 @@ private String buildFinalAsgName(String clusterName) {
public void restageApplication(String appGuid) {
safelyCall(() -> api.restageApplication(appGuid, ""));
}

public ProcessStats.State getAppState(String guid) {
return processes
.getProcessState(guid)
.orElseGet(
() ->
safelyCall(() -> api.findById(guid))
.filter(
application ->
CloudFoundryServerGroup.State.STARTED.equals(
CloudFoundryServerGroup.State.valueOf(application.getState())))
.map(appState -> ProcessStats.State.RUNNING)
.orElse(ProcessStats.State.DOWN));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ public interface CloudFoundryClient {
Tasks getTasks();

Logs getLogs();

Processes getProcesses();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
Expand All @@ -52,16 +53,17 @@ public class HttpCloudFoundryClient implements CloudFoundryClient {
private final String user;
private final String password;
private Logger logger = LoggerFactory.getLogger(HttpCloudFoundryClient.class);
private AuthenticationService uaaService;
private Spaces spaces;
private Organizations organizations;
private Domains domains;
private Routes routes;
private Applications applications;
private ServiceInstances serviceInstances;
private ServiceKeys serviceKeys;
private Tasks tasks;
private Logs logs;
@Getter private AuthenticationService uaaService;
@Getter private Spaces spaces;
@Getter private Organizations organizations;
@Getter private Domains domains;
@Getter private Routes routes;
@Getter private Applications applications;
@Getter private ServiceInstances serviceInstances;
@Getter private ServiceKeys serviceKeys;
@Getter private Tasks tasks;
@Getter private Logs logs;
@Getter private Processes processes;

public HttpCloudFoundryClient(
String account,
Expand Down Expand Up @@ -119,13 +121,16 @@ public HttpCloudFoundryClient(

this.organizations = new Organizations(retrofit.create(OrganizationService.class));
this.spaces = new Spaces(retrofit.create(SpaceService.class), organizations);
this.processes = new Processes(retrofit.create(ProcessesService.class));

this.applications =
new Applications(
account,
appsManagerUri,
metricsUri,
retrofit.create(ApplicationService.class),
spaces,
processes,
resultsPerPage,
forkJoinPool);
this.domains = new Domains(retrofit.create(DomainService.class), organizations);
Expand Down Expand Up @@ -192,49 +197,4 @@ public X509Certificate[] getAcceptedIssuers() {
}
return builder.build();
}

@Override
public Spaces getSpaces() {
return spaces;
}

@Override
public Organizations getOrganizations() {
return organizations;
}

@Override
public Domains getDomains() {
return domains;
}

@Override
public Routes getRoutes() {
return routes;
}

@Override
public Applications getApplications() {
return applications;
}

@Override
public ServiceInstances getServiceInstances() {
return serviceInstances;
}

@Override
public ServiceKeys getServiceKeys() {
return serviceKeys;
}

@Override
public Tasks getTasks() {
return tasks;
}

@Override
public Logs getLogs() {
return logs;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2021 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.spinnaker.clouddriver.cloudfoundry.client;

import static com.netflix.spinnaker.clouddriver.cloudfoundry.client.CloudFoundryClientUtils.safelyCall;

import com.netflix.spinnaker.clouddriver.cloudfoundry.client.api.ProcessesService;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Process;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.ProcessStats;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.ScaleProcess;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.UpdateProcess;
import groovy.util.logging.Slf4j;
import java.util.Optional;
import javax.annotation.Nullable;
import lombok.RequiredArgsConstructor;

@Slf4j
@RequiredArgsConstructor
public class Processes {

private final ProcessesService api;

public void scaleProcess(
String guid,
@Nullable Integer instances,
@Nullable Integer memInMb,
@Nullable Integer diskInMb)
throws CloudFoundryApiException {
if ((memInMb == null && diskInMb == null && instances == null)
|| (Integer.valueOf(0).equals(memInMb)
&& Integer.valueOf(0).equals(diskInMb)
&& Integer.valueOf(0).equals(instances))) {
return;
}
safelyCall(() -> api.scaleProcess(guid, new ScaleProcess(instances, memInMb, diskInMb)));
}

public Optional<Process> findProcessById(String guid) {
return safelyCall(() -> api.findProcessById(guid));
}

public void updateProcess(
String guid,
@Nullable String command,
@Nullable String healthCheckType,
@Nullable String healthCheckEndpoint)
throws CloudFoundryApiException {
final Process.HealthCheck healthCheck =
healthCheckType != null ? new Process.HealthCheck().setType(healthCheckType) : null;
if (healthCheckEndpoint != null && !healthCheckEndpoint.isEmpty() && healthCheck != null) {
healthCheck.setData(new Process.HealthCheckData().setEndpoint(healthCheckEndpoint));
}
if (command != null && command.isEmpty()) {
throw new IllegalArgumentException(
"Buildpack commands cannot be empty. Please specify a custom command or set it to null to use the original buildpack command.");
}

safelyCall(() -> api.updateProcess(guid, new UpdateProcess(command, healthCheck)));
}

@Nullable
public Optional<ProcessStats.State> getProcessState(String guid) throws CloudFoundryApiException {
return safelyCall(() -> api.findProcessStatsById(guid))
.map(pr -> pr.getResources().stream().findAny().map(ProcessStats::getState))
.orElse(Optional.empty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v2.Page;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.*;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Package;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Process;
import java.util.List;
import java.util.Map;
import okhttp3.MultipartBody;
Expand Down Expand Up @@ -78,19 +77,6 @@ Call<ResponseBody> unmapRoute(
@DELETE("/v2/apps/{guid}/instances/{index}")
Call<ResponseBody> deleteAppInstance(@Path("guid") String guid, @Path("index") String index);

@POST("/v3/processes/{guid}/actions/scale")
Call<ResponseBody> scaleApplication(
@Path("guid") String guid, @Body ScaleApplication scaleApplication);

@PATCH("/v3/processes/{guid}")
Call<Process> updateProcess(@Path("guid") String guid, @Body UpdateProcess updateProcess);

@GET("/v3/processes/{guid}")
Call<Process> findProcessById(@Path("guid") String guid);

@GET("/v3/processes/{guid}/stats")
Call<ProcessResources> findProcessStatsById(@Path("guid") String guid);

@POST("/v3/apps")
Call<Application> createApplication(@Body CreateApplication application);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2021 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.spinnaker.clouddriver.cloudfoundry.client.api;

import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.Process;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.ProcessResources;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.ScaleProcess;
import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.v3.UpdateProcess;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.*;

public interface ProcessesService {

@POST("/v3/processes/{guid}/actions/scale")
Call<ResponseBody> scaleProcess(@Path("guid") String guid, @Body ScaleProcess scaleProcess);

@PATCH("/v3/processes/{guid}")
Call<Process> updateProcess(@Path("guid") String guid, @Body UpdateProcess updateProcess);

@GET("/v3/processes/{guid}")
Call<Process> findProcessById(@Path("guid") String guid);

@GET("/v3/processes/{guid}/stats")
Call<ProcessResources> findProcessStatsById(@Path("guid") String guid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
@RequiredArgsConstructor
@Getter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ScaleApplication {
public class ScaleProcess {
@Nullable private final Integer instances;

@Nullable private final Integer memoryInMb;
Expand Down
Loading

0 comments on commit 65e5e52

Please sign in to comment.