Skip to content

Commit fa5aede

Browse files
committed
[LB-54]: Create a system to expose container and build logs to launchers
1 parent eb846c5 commit fa5aede

File tree

13 files changed

+216
-15
lines changed

13 files changed

+216
-15
lines changed

.env-example

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ MONGODB_USERNAME=
1111
MONGODB_PASSWORD=
1212

1313
# Internal Clients
14-
AUTH_CLIENT_URL=https://api-auth.launchblock.gg
15-
ENVIRONMENTS_CLIENT_URL=
16-
LIFECYCLES_CLIENT_URL=
17-
PROJECTS_CLIENT_URL=
14+
AUTH_CLIENT_URL=https://auth.launchblock.gg
15+
CONTAINERS_CLIENT_UR=https://api-auth.launchblock.gg
16+
ENVIRONMENTS_CLIENT_URL=https://api-auth.launchblock.gg
1817
GITHUB_CLIENT_URL=https://github-app.launchblock.gg
19-
SCALING_CLIENT_URL=
18+
LIFECYCLES_CLIENT_URL=https://api-auth.launchblock.gg
19+
LOGS_CLIENT_URL=https://api-auth.launchblock.gg

kubernetes-deployment.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ spec:
3939
value: "http://api-scaler"
4040
- name: SCALING_CLIENT_URL
4141
value: "http://api-scaler"
42+
- name: LOGS_CLIENT_URL
43+
value: "http://api-logs"
4244
- name: MONGODB_CONNECTION_STRING
4345
value: "mongodb://mongodb.launchblock-services.svc.cluster.local:27017"
4446
- name: MONGODB_USERNAME
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package gg.launchblock.api.clients.base;
2+
3+
import gg.launchblock.api.filter.ForwardedHeadersFilter;
4+
import gg.launchblock.api.models.lifecycles.LifecycleStage;
5+
import gg.launchblock.api.models.logs.LogLevel;
6+
import gg.launchblock.api.models.logs.request.LogRequestModel;
7+
import gg.launchblock.api.models.logs.response.LogResponseModel;
8+
import io.smallrye.mutiny.Uni;
9+
import jakarta.validation.constraints.NotNull;
10+
import jakarta.ws.rs.*;
11+
import jakarta.ws.rs.core.MediaType;
12+
import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders;
13+
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
14+
15+
import java.util.List;
16+
import java.util.UUID;
17+
18+
@RegisterRestClient(configKey = "logs")
19+
@Path("/logs")
20+
@RegisterClientHeaders(ForwardedHeadersFilter.class)
21+
public interface LogsClient {
22+
23+
@Produces(MediaType.APPLICATION_JSON)
24+
@Consumes(MediaType.APPLICATION_JSON)
25+
@GET
26+
Uni<List<LogResponseModel>> listLogs(
27+
@NotNull @QueryParam("stage") final LifecycleStage stage,
28+
@QueryParam("lifecycle_identifier") final UUID deploymentIdentifier,
29+
@QueryParam("container_identifier") final UUID containerIdentifier,
30+
@QueryParam("message") final String message,
31+
@QueryParam("log_level") final LogLevel logLevel,
32+
@QueryParam("page") @DefaultValue("1") final int page,
33+
@QueryParam("per_page") @DefaultValue("50") final int perPage);
34+
35+
@Produces(MediaType.APPLICATION_JSON)
36+
@Consumes(MediaType.APPLICATION_JSON)
37+
@GET
38+
Uni<List<LogResponseModel>> listLogs(
39+
@NotNull @QueryParam("stage") final LifecycleStage stage,
40+
@QueryParam("lifecycle_identifier") final UUID deploymentIdentifier,
41+
@QueryParam("message") final String message,
42+
@QueryParam("log_level") final LogLevel logLevel,
43+
@QueryParam("page") @DefaultValue("1") final int page,
44+
@QueryParam("per_page") @DefaultValue("50") final int perPage);
45+
46+
@Produces(MediaType.APPLICATION_JSON)
47+
@Consumes(MediaType.APPLICATION_JSON)
48+
@POST
49+
@Path("/{lifecycle_identifier}")
50+
Uni<List<LogResponseModel>> createLogs(
51+
@NotNull @PathParam("lifecycle_identifier") final UUID lifecycleIdentifier,
52+
@NotNull final List<LogRequestModel> logModels);
53+
54+
}

src/main/java/gg/launchblock/api/constants/Permission.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public enum Permission {
4747
// scaling
4848
VIEW_SCALE_GOALS("view_scale_goals", "View progress towards set down scaling parameters"),
4949

50+
// logs
51+
VIEW_LOGS("view_logs", "View the logs attached to a lifecycle"),
52+
CREATE_LOGS("create_logs", "Create a set of logs attached to a container within a deployment", false),
53+
5054
// variables (env vars)
5155
CREATE_VARIABLES("create_variables", "Create environment variables on a project or environment"),
5256
VIEW_VARIABLES("view_variables", "View variables attached to a project or environment"),

src/main/java/gg/launchblock/api/endpoint/LifecyclesEndpoint.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,28 @@
22

33
import gg.launchblock.api.annotations.base.permissions.RequiredPermission;
44
import gg.launchblock.api.clients.base.LifecyclesClient;
5+
import gg.launchblock.api.clients.base.LogsClient;
56
import gg.launchblock.api.clients.base.VariablesClient;
67
import gg.launchblock.api.constants.Permission;
78
import gg.launchblock.api.exception.base.BuiltInExceptions;
89
import gg.launchblock.api.exception.base.LaunchBlockException;
10+
import gg.launchblock.api.models.lifecycles.LifecycleStage;
911
import gg.launchblock.api.models.lifecycles.response.LifecycleConfigurationResponseModel;
1012
import gg.launchblock.api.models.lifecycles.response.LifecycleResponseModel;
13+
import gg.launchblock.api.models.logs.LogLevel;
14+
import gg.launchblock.api.models.logs.response.LogResponseModel;
1115
import gg.launchblock.api.models.variables.response.VariableResponseModel;
1216
import gg.launchblock.api.user.base.RequestContextHolder;
1317
import io.smallrye.mutiny.Uni;
1418
import jakarta.inject.Inject;
19+
import jakarta.validation.constraints.Max;
20+
import jakarta.validation.constraints.Min;
1521
import jakarta.validation.constraints.NotNull;
1622
import jakarta.ws.rs.*;
1723
import jakarta.ws.rs.core.MediaType;
1824
import lombok.RequiredArgsConstructor;
1925
import org.eclipse.microprofile.openapi.annotations.Operation;
26+
import org.eclipse.microprofile.openapi.annotations.media.Schema;
2027
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
2128
import org.eclipse.microprofile.rest.client.inject.RestClient;
2229

@@ -34,6 +41,10 @@ public class LifecyclesEndpoint {
3441
@Inject
3542
LifecyclesClient lifecyclesClient;
3643

44+
@RestClient
45+
@Inject
46+
LogsClient logsClient;
47+
3748
@RestClient
3849
@Inject
3950
VariablesClient variablesClient;
@@ -44,8 +55,8 @@ public class LifecyclesEndpoint {
4455
@GET
4556
@RequiredPermission(value = Permission.VIEW_LIFECYCLES)
4657
public Uni<List<LifecycleResponseModel>> listLifecycles(
47-
@QueryParam("per_page") @DefaultValue("25") final int perPage,
48-
@QueryParam("page") @DefaultValue("1") final int page,
58+
@QueryParam("per_page") @DefaultValue("25") @Min(1) @Max(200) final int perPage,
59+
@QueryParam("page") @DefaultValue("1") @Min(value = 1) final int page,
4960
@NotNull @PathParam("project_identifier") final UUID projectIdentifier) {
5061
if (this.contextHolder.getEnvironmentIdentifier() == null) {
5162
throw new LaunchBlockException(BuiltInExceptions.ESSENTIAL_HEADERS_MISSING, "environment-identifier");
@@ -98,6 +109,22 @@ public Uni<LifecycleResponseModel> rollbackLifecycle(
98109
return this.lifecyclesClient.rollbackLifecycle(identifier);
99110
}
100111

112+
@Operation(operationId = "Logs.list", summary = "Get a list of logs attached to a lifecycle")
113+
@Produces(MediaType.APPLICATION_JSON)
114+
@Consumes(MediaType.APPLICATION_JSON)
115+
@GET
116+
@Path("/{identifier}/logs")
117+
@RequiredPermission(value = Permission.VIEW_LOGS)
118+
public Uni<List<LogResponseModel>> listLogs(
119+
@NotNull @PathParam("identifier") final UUID lifecycleIdentifier,
120+
@QueryParam("lifecycle_stage") @DefaultValue("DEPLOY") final LifecycleStage lifecycleStage,
121+
@QueryParam("message") @Schema(description = "Find logs where this is included in the message field") final String message,
122+
@QueryParam("log_level") final LogLevel logLevel,
123+
@QueryParam("page") @DefaultValue("1") @Min(value = 1) final int page,
124+
@QueryParam("per_page") @DefaultValue("50") @Min(value = 1) @Max(value = 100) final int perPage) {
125+
return this.logsClient.listLogs(lifecycleStage, lifecycleIdentifier, message, logLevel, page, perPage);
126+
}
127+
101128
@Operation(operationId = "ProjectVariables.list", summary = "List all variables in a deployed version of the project")
102129
@Produces(MediaType.APPLICATION_JSON)
103130
@Consumes(MediaType.APPLICATION_JSON)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package gg.launchblock.api.endpoint;
2+
3+
import gg.launchblock.api.annotations.base.permissions.RequiredPermission;
4+
import gg.launchblock.api.clients.base.LogsClient;
5+
import gg.launchblock.api.constants.Permission;
6+
import gg.launchblock.api.models.logs.request.LogRequestModel;
7+
import gg.launchblock.api.models.logs.response.LogResponseModel;
8+
import io.smallrye.mutiny.Uni;
9+
import jakarta.inject.Inject;
10+
import jakarta.validation.Valid;
11+
import jakarta.validation.constraints.NotNull;
12+
import jakarta.ws.rs.*;
13+
import jakarta.ws.rs.core.MediaType;
14+
import lombok.RequiredArgsConstructor;
15+
import org.eclipse.microprofile.openapi.annotations.Operation;
16+
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
17+
import org.eclipse.microprofile.rest.client.inject.RestClient;
18+
19+
import java.util.List;
20+
import java.util.UUID;
21+
22+
@Tag(name = "Logs", description = "Fetch and query logs being emitted from projects")
23+
@Path("/v1/logs")
24+
@RequiredArgsConstructor
25+
public class LogsEndpoint {
26+
27+
@Inject
28+
@RestClient
29+
LogsClient logsClient;
30+
31+
@Operation(operationId = "Logs.create", summary = "Create a list of logs coming from a container")
32+
@Produces(MediaType.APPLICATION_JSON)
33+
@Consumes(MediaType.APPLICATION_JSON)
34+
@POST
35+
@Path("/lifecycle/{lifecycle_identifier}")
36+
@RequiredPermission(value = Permission.CREATE_LOGS, containerOnly = true)
37+
public Uni<List<LogResponseModel>> createLogs(
38+
@NotNull @PathParam("lifecycle_identifier") final UUID lifecycleIdentifier,
39+
@Valid @NotNull final List<LogRequestModel> logModels) {
40+
return this.logsClient.createLogs(lifecycleIdentifier, logModels);
41+
}
42+
43+
}

src/main/java/gg/launchblock/api/models/lifecycles/response/LifecycleResponseModel.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,4 @@ public class LifecycleResponseModel {
2828
@Schema(description = "The current stage of the lifecycle")
2929
public LifecycleStage stage;
3030

31-
public String getBuildLogs() {
32-
return "https://api.launchblock.gg/v1/logs/" + this.identifier + "?lifecycle_stage=build";
33-
}
34-
35-
public String getDeployLogs() {
36-
return "https://api.launchblock.gg/v1/logs/" + this.identifier + "?lifecycle_stage=deploy";
37-
}
38-
3931
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package gg.launchblock.api.models.logs;
2+
3+
public enum LogLevel {
4+
DEBUG,
5+
INFO,
6+
NOTICE,
7+
WARN,
8+
ERROR,
9+
CRITICAL,
10+
ALERT,
11+
EMERGENCY,
12+
TRACE,
13+
FATAL
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package gg.launchblock.api.models.logs.request;
2+
3+
import gg.launchblock.api.models.lifecycles.LifecycleStage;
4+
import gg.launchblock.api.models.logs.LogLevel;
5+
import lombok.Data;
6+
import org.eclipse.microprofile.openapi.annotations.media.Schema;
7+
8+
import java.time.Instant;
9+
10+
@Data
11+
public class LogRequestModel {
12+
13+
@Schema(description = "The log message content")
14+
private String message;
15+
16+
@Schema(description = "The severity level of the log entry")
17+
private LogLevel level;
18+
19+
private LifecycleStage stage;
20+
21+
@Schema(description = "Timestamp when the log entry was created", example = "2024-03-20T10:15:30Z")
22+
private Instant timestamp;
23+
24+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package gg.launchblock.api.models.logs.response;
2+
3+
import gg.launchblock.api.models.lifecycles.LifecycleStage;
4+
import gg.launchblock.api.models.logs.LogLevel;
5+
import lombok.Data;
6+
import org.eclipse.microprofile.openapi.annotations.media.Schema;
7+
8+
import java.time.Instant;
9+
import java.util.UUID;
10+
11+
@Data
12+
public class LogResponseModel {
13+
14+
@Schema(description = "Unique identifier for the lifecycle instance", example = "123e4567-e89b-12d3-a456-426614174000")
15+
private UUID lifecycleIdentifier;
16+
17+
@Schema(description = "Unique identifier for the container instance", example = "123e4567-e89b-12d3-a456-426614174000")
18+
private UUID containerIdentifier;
19+
20+
@Schema(description = "The log message content")
21+
private String message;
22+
23+
@Schema(description = "The severity level of the log entry")
24+
private LogLevel level;
25+
26+
private LifecycleStage stage;
27+
28+
@Schema(description = "Timestamp when the log entry was created", example = "2024-03-20T10:15:30Z")
29+
private Instant timestamp;
30+
31+
}

0 commit comments

Comments
 (0)