Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor(cloudfoundry): move processes into their own services #5316

Merged
merged 14 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice removal.

}
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);

Copy link
Contributor

Choose a reason for hiding this comment

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

Is the plan to move other methods to different interfaces (like one interface for routes, one for packages, etc)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think as they get big enough it makes sense. Overall I think our nomenclature should be as similar to the cloud foundry api as possible. Another example, I renamed "scaleApplication" to "scaleProcess" since thats actually whats happening (v3/processes/:guid/actions/scale). That could cause confusion for someone looking at it for the first time if they don't have the context

@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