Skip to content

Commit

Permalink
Merge pull request #12 from saragluna/xiada/patch-3
Browse files Browse the repository at this point in the history
Support built-in opentelemetry
  • Loading branch information
JonathanGiles authored Jul 11, 2024
2 parents 13048c4 + 00e79ac commit 19422c9
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,8 @@ private void introspectPomXml(SpringProject project, String pomXmlPath, Map<Stri
String buildpacks = getPluginConfiguration(plugin, "image.buildpacks");
// FIXME this doesn't cover all cases, but it's a start
if (buildpacks == null) {
LOGGER.warning("""
OpenTelemetry is enabled for [%s], it requires the Spring Boot Maven Plugin to be configured with the following:
<configuration>
<image>
<buildpacks>
<buildpack>paketo-buildpacks/java</buildpack>
<buildpack>gcr.io/paketo-buildpacks/opentelemetry</buildpack>
</buildpacks>
<env>
<BP_OPENTELEMETRY_ENABLED>true</BP_OPENTELEMETRY_ENABLED>
</env>
</image>
</configuration>
""".formatted(project.getName()));
LOGGER.fine("OpenTelemetry is enabled for [%s], but the Spring Boot Maven Plugin is not configured with buildpacks.".formatted(project.getName()));
outputEnvs.put("BUILD_ADD_OTEL_AGENT", "true");
}
}
// FIXME We could add more options here, like -Dspring-boot.build-image.imageName=...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@
import com.microsoft.aspire.DistributedApplication;
import com.microsoft.aspire.extensions.spring.implementation.SpringDeploymentStrategy;
import com.microsoft.aspire.extensions.spring.implementation.SpringIntrospector;
import com.microsoft.aspire.utils.json.RelativePath;
import com.microsoft.aspire.utils.json.RelativePathSerializer;
import com.microsoft.aspire.resources.Container;
import com.microsoft.aspire.resources.DockerFile;
import com.microsoft.aspire.resources.ResourceType;
import com.microsoft.aspire.resources.traits.IntrospectiveResource;
import com.microsoft.aspire.resources.traits.ResourceWithTemplate;
import com.microsoft.aspire.utils.FileUtilities;
import com.microsoft.aspire.utils.json.RelativePath;
import com.microsoft.aspire.utils.json.RelativePathSerializer;
import com.microsoft.aspire.utils.templates.TemplateEngine;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SpringProject extends Container<SpringProject> implements IntrospectiveResource {
public class SpringProject extends Container<SpringProject>
implements ResourceWithTemplate<SpringProject>, IntrospectiveResource {
private static final ResourceType SPRING_PROJECT = ResourceType.fromString("project.spring.v0");

@Valid
Expand All @@ -37,7 +42,9 @@ public class SpringProject extends Container<SpringProject> implements Introspec

@JsonIgnore
private boolean openTelemetryEnabled;


private String templateDockerfilePath;

public SpringProject(String name) {
super(SPRING_PROJECT, name, null);
withEnvironment("spring.application.name", name);
Expand All @@ -46,6 +53,10 @@ public SpringProject(String name) {
withPath(name);
}

private SpringProject(String name, ResourceType type) {
super(type, name, null);
}

@Override
public void onResourcePrecommit() {
super.onResourcePrecommit();
Expand All @@ -72,6 +83,7 @@ public void introspect() {
if (outputEnvs.containsKey("BUILD_IMAGE")) {
withImage(outputEnvs.get("BUILD_IMAGE"));
}

// but, we also look in the strategies to see if we found a dockerfile strategy, as in that case we transform
// this entire output from a Spring project resource into a dockerfile resource
strategies.stream()
Expand All @@ -91,6 +103,33 @@ public void introspect() {

DistributedApplication.getInstance().substituteResource(this, dockerFile);
});

// if we need to rebuild the image with more attributes
if ("true".equals(outputEnvs.get("BUILD_ADD_OTEL_AGENT"))) {
strategies.stream()
.filter(s -> s.getType() == SpringDeploymentStrategy.DeploymentType.MAVEN_POM)
.findFirst().ifPresent(s -> {
String imageResourceName = getName() + "-image";
SpringProject containerImage = new SpringProject(imageResourceName, ResourceType.fromString("project.spring.image.v0"));
containerImage.strategies = strategies;
containerImage.withImage(outputEnvs.get("BUILD_IMAGE"))
.withPath(this.getPath());
DistributedApplication.getInstance().addResource(containerImage);

DockerFile<?> dockerFile = new DockerFile<>(getName());
String dockerFilePath = this.templateDockerfilePath;
String contextPath = Paths.get(dockerFilePath).getParent().toString();
this.copyInto(dockerFile);
dockerFile.withPath(dockerFilePath)
.withContext(contextPath)
.withExternalHttpEndpoints()
.withBuildArg("BASE_IMAGE", "${%s.image}".formatted(imageResourceName))
; // FIXME this is not really the context

DistributedApplication.getInstance().substituteResource(this, dockerFile);
}
);
}
}

/**
Expand Down Expand Up @@ -124,4 +163,30 @@ public boolean isOpenTelemetryEnabled() {
public SpringProject self() {
return this;
}

@Override
public List<TemplateFileOutput> processTemplate() {
if (openTelemetryEnabled) {
Map<String, Object> properties = new HashMap<>();
properties.put("BASE_IMAGE", "${BASE_IMAGE}");
final String templatePath = "/templates/opentelemetry/";
final String outputRootPath = "opentelemetry/";
List<TemplateDescriptor> templateFiles = TemplateDescriptorsBuilder.begin(templatePath, outputRootPath)
.with("Dockerfile")
.with("JAVA_TOOL_OPTIONS.append")
.with("JAVA_TOOL_OPTIONS.delim")
.build();

List<TemplateFileOutput> templateOutput = TemplateEngine.process(SpringProject.class, templateFiles, properties);

// Important - as noted in the javadoc - from the perspective of the API below, the paths are relative to the
// directory in which azd is running, NOT the output directory. These paths will then be transformed at
// serialization time to be relative to the output directory.
// This is slightly unfortunate, as we know the correct directory here, but we don't have a way to pass it.
templateDockerfilePath = FileUtilities.convertOutputPathToRootRelative(outputRootPath + "Dockerfile").toString();

return templateOutput;
}
return List.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

// We conditionally open up the template files to the apphost, so it can write them out
opens templates.eureka to com.microsoft.aspire;
opens templates.opentelemetry to com.microsoft.aspire;
exports com.microsoft.aspire.extensions.spring.implementation;

provides com.microsoft.aspire.Extension with SpringExtension;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
RUN echo "Base image is ${BASE_IMAGE}"
MAINTAINER microsoft.com
USER root
ADD https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar /opentelemetry-javaagent.jar
# Install any necessary tools (if not already available in the base image)
RUN apt-get update && apt-get install -y \
sed \
&& rm -rf /var/lib/apt/lists/* \
USER cnb
RUN API_VERSION=$(sed -n '/^[[:space:]]*api = "[^"]*"/{s/^[[:space:]]*api = "\([^"]*\)".*/\1/; /^[^[:space:]]*$/ p; q;}' /layers/config/metadata.toml) \
&& echo "API_VERSION=$API_VERSION" \
# Create or append content to metadata.toml using echo and redirection \
&& { \
echo ''; \
echo '[[buildpacks]]'; \
echo ' id="aspire-otel"'; \
echo ' version = "0.0.1"'; \
echo " api = \"$API_VERSION\""; \
} >> /layers/config/metadata.toml
RUN mkdir -p /layers/aspire-otel/jre/env.launch
RUN cat /layers/config/metadata.toml
COPY JAVA_TOOL_OPTIONS.delim /layers/aspire-otel/jre/env.launch/JAVA_TOOL_OPTIONS.delim
COPY JAVA_TOOL_OPTIONS.append /layers/aspire-otel/jre/env.launch/JAVA_TOOL_OPTIONS.append
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-javaagent:/opentelemetry-javaagent.jar
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private void writeManifest(DistributedApplication app) {
}

// run the precommit lifecycle hook on all resources
app.manifest.getResources().values().forEach(ResourceWithLifecycle::onResourcePrecommit);
app.manifest.getResources().values().iterator().forEachRemaining(ResourceWithLifecycle::onResourcePrecommit);

LOGGER.info("Validating models...");
// Firstly, disable the info logging messages that are printed by Hibernate Validator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

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

/*
{
"type": "object",
Expand Down Expand Up @@ -61,6 +64,9 @@ public class DockerFile<T extends DockerFile<T>> extends Resource<T>
@RelativePath
private String context;

@JsonProperty("buildArgs")
private List<String[]> buildArgs;

public DockerFile(String name) {
this(name, null, null);
}
Expand Down Expand Up @@ -100,6 +106,14 @@ public T withContext(String context) {
this.context = context;
return self();
}

public T withBuildArg(String key, String value) {
if (buildArgs == null) {
buildArgs = new ArrayList<>();
}
buildArgs.add(new String[] { key, value });
return self();
}

@Override
public T self() {
Expand Down
11 changes: 0 additions & 11 deletions samples/storage-explorer/storage-explorer/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,6 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<buildpacks>
<buildpack>paketo-buildpacks/java</buildpack>
<buildpack>gcr.io/paketo-buildpacks/opentelemetry</buildpack>
</buildpacks>
<env>
<BP_OPENTELEMETRY_ENABLED>true</BP_OPENTELEMETRY_ENABLED>
</env>
</image>
</configuration>
</plugin>
</plugins>
</build>
Expand Down

0 comments on commit 19422c9

Please sign in to comment.