Skip to content

Commit

Permalink
Packaging initial infrastructure (#133)
Browse files Browse the repository at this point in the history
Not complete but enough to review as part 1 😄 

* Introduces our own version of `OpenTelemetry.AutoInstrumentation`
named `Elastic.OpenTelemetry.AutoInstrumentation`.

Through a bit of msbuild magic we redistribute `instrument.{sh|cmd}` as
`_instrument.{sh|cmd}` and introduce our own `instrument.{sh|cmd}` that
sets the appropiate plugin environment variable before calling the
opentelemetry instrument script.

This allows the invocation to be exactly the same no matter if our
distribution or the vanilla opentelemetry package is installed. A user
can move from or away from Elastic without having to touch orchestration
work. Simply installing the package is enough to move to or away from
us.

* This PR includes the start of a `./build.sh redistribute` build
command that redistributes the opentelemetry autoinstrumentation zip
files that include our AutoInstrumentation plugin.

A follow up of this PR will focus on creating Elastic versions of the
global auto instrumentation installation scripts and ensuring these get
staged as part of the release.
  • Loading branch information
Mpdreamz authored Jul 29, 2024
1 parent 3f2d2fb commit f842bb7
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Elastic.OpenTelemetry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.AutoInstrumentation
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoInstrumentation.IntegrationTests", "tests\AutoInstrumentation.IntegrationTests\AutoInstrumentation.IntegrationTests.csproj", "{782E4DC1-8186-4BAC-B2F4-89E6DF22A4DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.OpenTelemetry.AutoInstrumentation", "src\Elastic.OpenTelemetry.AutoInstrumentation\Elastic.OpenTelemetry.AutoInstrumentation.csproj", "{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -107,6 +109,10 @@ Global
{782E4DC1-8186-4BAC-B2F4-89E6DF22A4DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{782E4DC1-8186-4BAC-B2F4-89E6DF22A4DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{782E4DC1-8186-4BAC-B2F4-89E6DF22A4DD}.Release|Any CPU.Build.0 = Release|Any CPU
{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -123,6 +129,7 @@ Global
{A3D1ED4D-863B-45D7-9829-305DD33B4CE5} = {4E95C87B-655B-4BC3-8F2A-DF06B7AAB7E9}
{F3AA76EC-C7D8-42DA-947D-4376B6562772} = {4E95C87B-655B-4BC3-8F2A-DF06B7AAB7E9}
{782E4DC1-8186-4BAC-B2F4-89E6DF22A4DD} = {AAD39891-0B70-47FA-A212-43E1AAE5DF56}
{B1CA9165-89D9-4D6E-AFEF-5434A8D8A672} = {E622CFF2-C6C4-40FB-BE42-7C4F2B38B75A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {573B2B5F-8CBB-4D52-A55A-4E65E282AAFB}
Expand Down
1 change: 1 addition & 0 deletions Elastic.OpenTelemetry.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ See the LICENSE file in the project root for more information</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Faas/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Instrumentations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mountinfo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=netfx/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nonsampled/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=otel/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Otlp/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
2 changes: 2 additions & 0 deletions build/build.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="Argu" Version="6.1.4"/>
<PackageReference Include="Bullseye" Version="4.2.1"/>
<PackageReference Include="Octokit" Version="13.0.1" />
<PackageReference Include="Proc.Fs" Version="0.8.1"/>
<PackageReference Include="Fake.Tools.Git" Version="5.20.3"/>
<PackageReference Remove="FSharp.Core"/>
Expand All @@ -21,6 +22,7 @@
<ItemGroup>
<Compile Include="scripts\BuildInformation.fs"/>
<Compile Include="scripts\CommandLine.fs"/>
<Compile Include="scripts\Packaging.fs" />
<Compile Include="scripts\Targets.fs"/>
<Compile Include="scripts\Program.fs"/>
<None Include="**\*"/>
Expand Down
1 change: 1 addition & 0 deletions build/scripts/BuildInformation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ open Fake.Tools.Git
type BuildConfiguration =
static member ValidateAssemblyName = false
static member GenerateApiChanges = false
static member OpenTelemetryAutoInstrumentationVersion = SemVer.parse("1.7.0")

type Software =
static member Organization = "elastic"
Expand Down
2 changes: 2 additions & 0 deletions build/scripts/CommandLine.fs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Build =
| [<CliPrefix(CliPrefix.None);Hidden;SubCommand>] ValidatePackages
| [<CliPrefix(CliPrefix.None);Hidden;SubCommand>] GenerateReleaseNotes
| [<CliPrefix(CliPrefix.None);Hidden;SubCommand>] GenerateApiChanges
| [<CliPrefix(CliPrefix.None);Hidden;SubCommand>] Redistribute
| [<CliPrefix(CliPrefix.None);SubCommand>] Release

| [<Inherit;AltCommandLine("-s")>] Single_Target
Expand Down Expand Up @@ -63,6 +64,7 @@ with
| ValidateLicenses
| ValidatePackages
| GenerateReleaseNotes
| Redistribute
| GenerateApiChanges -> "Undocumented, dependent target"

// flags
Expand Down
111 changes: 111 additions & 0 deletions build/scripts/Packaging.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information


module Packaging

open System
open System.IO
open System.IO.Compression
open System.Net.Http
open Argu
open BuildInformation
open CommandLine
open Octokit

let private otelAutoVersion = BuildConfiguration.OpenTelemetryAutoInstrumentationVersion;

let private downloadFolder = Path.Combine(".artifacts", "otel-distribution", otelAutoVersion.AsString) |> Directory.CreateDirectory
let private distroFolder = Path.Combine(".artifacts", "elastic-distribution", otelAutoVersion.AsString) |> Directory.CreateDirectory

let private fileInfo (directory: DirectoryInfo) file = Path.Combine(directory.FullName, file) |> FileInfo
let private downloadFile (asset: ReleaseAsset) = fileInfo downloadFolder asset.Name
let private stageFile (asset: ReleaseAsset) = fileInfo downloadFolder (asset.Name.Replace("opentelemetry", "stage"))
let private distroFile (asset: ReleaseAsset) = fileInfo distroFolder (asset.Name.Replace("opentelemetry", "elastic"))

let pluginFiles tfm =
["dll"; "pdb"; "xml"]
|> List.map(fun e -> $"Elastic.OpenTelemetry.%s{e}")
|> List.map(fun f -> Path.Combine(".artifacts", "bin", "Elastic.OpenTelemetry", $"release_%s{tfm}", "", f))
|> List.map(fun f -> FileInfo(f))


/// downloads the artifacts if they don't already exist locally
let downloadArtifacts (_:ParseResults<Build>) =
let client = GitHubClient(ProductHeaderValue("Elastic.OpenTelemetry"))
let token = Environment.GetEnvironmentVariable("GITHUB_TOKEN")
if not(String.IsNullOrWhiteSpace(token)) then
Console.WriteLine($"using GITHUB_TOKEN");
let tokenAuth = Credentials(token);
client.Credentials <- tokenAuth

let assets =
async {
let! release = client.Repository.Release.Get("open-telemetry", "opentelemetry-dotnet-instrumentation", $"v{otelAutoVersion.AsString}") |> Async.AwaitTask;
Console.WriteLine($"Release %s{release.Name} has %i{release.Assets.Count} assets");
return release.Assets
|> Seq.map (fun asset -> (asset, downloadFile asset))
|> Seq.toList
} |> Async.RunSynchronously

async {
use httpClient = new HttpClient()
assets
|> Seq.filter (fun (_, f) -> not f.Exists)
|> Seq.iter (fun (asset, f) ->
async {
Console.WriteLine($"Retrieving {asset.Name}");
let! fileData = httpClient.GetByteArrayAsync(asset.BrowserDownloadUrl) |> Async.AwaitTask
Console.WriteLine($"Saveing %i{fileData.Length} bytes to {f.FullName}")
File.WriteAllBytes(f.FullName, fileData)
f.Refresh()
} |> Async.RunSynchronously
)
} |> Async.RunSynchronously
assets

let injectPluginFiles (asset: ReleaseAsset) (stagedZip: FileInfo) tfm target =
use zipArchive = ZipFile.Open(stagedZip.FullName, ZipArchiveMode.Update)
pluginFiles tfm |> List.iter(fun f ->
printfn $"Staging zip: %s{asset.Name}, Adding: %s{f.Name} (%s{tfm}_ to %s{target}"
zipArchive.CreateEntryFromFile(f.FullName, Path.Combine(target, f.Name)) |> ignore
)

/// moves artifacts from open-distribution to elastic-distribution and renames them to `staged-dotnet-instrumentation*`.
/// staged meaning we haven't injected our opentelemetry dll into the zip yet,
let stageArtifacts (assets:List<ReleaseAsset * FileInfo>) =
let stagedZips =
assets
|> List.filter(fun (a, _) -> a.Name.EndsWith(".zip"))
|> List.map(fun (z, f) ->
let stage = stageFile z
z, f.CopyTo(stage.FullName, true)
)

stagedZips |> List.iter (fun (asset, path) ->

injectPluginFiles asset path "netstandard2.1" "net"
if asset.Name.EndsWith("-windows.zip") then
injectPluginFiles asset path "net462" "netfx"

let distro = distroFile asset
path.MoveTo(distro.FullName, true)
distro.Refresh()

printfn $"Created: %s{distro.FullName}"
)
stagedZips



let redistribute (arguments:ParseResults<Build>) =
let assets = downloadArtifacts arguments
let staged = stageArtifacts assets

printfn ""
assets |> List.iter (fun (asset, path) ->
printfn "Asset: %s" asset.Name
)


1 change: 1 addition & 0 deletions build/scripts/Targets.fs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ let Setup (parsed:ParseResults<Build>) =
| ValidatePackages -> Build.Step validatePackages
| GenerateReleaseNotes -> Build.Step generateReleaseNotes
| GenerateApiChanges -> Build.Step generateApiChanges
| Redistribute -> Build.Step Packaging.redistribute

// flags
| Single_Target
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net462</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>True</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry.AutoInstrumentation" Version="1.7.0" GeneratePathProperty="true" PrivateAssets="contentfiles" />
</ItemGroup>

<ItemGroup>
<Content Remove="README.md" />

<!-- ensure we remove the linked instrument.cmd from base OpenTelemetry.AutoInstrumentation
and link it as _instrument.cmd since we manually copy it over in the prebuild event -->
<Content Update="instrument.cmd" CopyToPublishDirectory="Never" CopyToOutputDirectory="Never" />
<Content Remove="instrument.cmd" />
<Content Include="_instrument.cmd" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always"
Pack="True" PackagePath="contentFiles/any/any/_instrument.cmd"/>
<Content Include="instrument.cmd" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always"
Pack="True" PackagePath="contentFiles/any/any/instrument.cmd"/>

<!-- ensure we remove the linked instrument.sh from base OpenTelemetry.AutoInstrumentation
and link it as _instrument.sh since we manually copy it over in the prebuild event -->

<Content Update="instrument.sh" CopyToPublishDirectory="Never" CopyToOutputDirectory="Never" />
<Content Remove="instrument.sh" />
<Content Include="_instrument.sh" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always"
Pack="True" PackagePath="contentFiles/any/any/_instrument.sh"/>
<Content Include="instrument.sh" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always"
Pack="True" PackagePath="contentFiles/any/any/instrument.sh"/>

<Content Include="instrument.props" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always"
Pack="True" PackagePath="build/elastic.opentelemetry.autoinstrumentation.props"/>

</ItemGroup>

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<!-- Copies the content files manually as physical files in the source repository -->
<!-- we manually repackage these as contentfiles (albeit renamed) -->
<Copy SourceFiles="$(PkgOpenTelemetry_AutoInstrumentation)/contentFiles/any/any/instrument.cmd" DestinationFiles="$(MSBuildThisFileDirectory)/_instrument.cmd"/>
<Copy SourceFiles="$(PkgOpenTelemetry_AutoInstrumentation)/contentFiles/any/any/instrument.sh" DestinationFiles="$(MSBuildThisFileDirectory)/_instrument.sh"/>
</Target>

<ItemGroup>
<ProjectReference Include="..\Elastic.OpenTelemetry\Elastic.OpenTelemetry.csproj" />
</ItemGroup>

</Project>
31 changes: 31 additions & 0 deletions src/Elastic.OpenTelemetry.AutoInstrumentation/_instrument.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@echo off
setlocal

:: This script is expected to be used in a build that specified a RuntimeIdentifier (RID)
set BASE_PATH=%~dp0

:: Settings for .NET Framework
set COR_ENABLE_PROFILING=1
set COR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318}
set COR_PROFILER_PATH=%BASE_PATH%OpenTelemetry.AutoInstrumentation.Native.dll

:: On .NET Framework automatic assembly redirection MUST be disabled. This setting
:: is ignored on .NET. This is necessary because the NuGet package doesn't bring
:: the pre-defined versions of the transitive dependencies used in the automatic
:: redirection. Instead the transitive dependencies versions are determined by
:: the NuGet version resolution algorithm when building the application.
set OTEL_DOTNET_AUTO_NETFX_REDIRECT_ENABLED=false

:: Settings for .NET
set ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper
set CORECLR_ENABLE_PROFILING=1
set CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318}
set CORECLR_PROFILER_PATH=%BASE_PATH%OpenTelemetry.AutoInstrumentation.Native.dll
set DOTNET_STARTUP_HOOKS=%BASE_PATH%OpenTelemetry.AutoInstrumentation.StartupHook.dll

:: Settings for OpenTelemetry
set OTEL_DOTNET_AUTO_HOME=%BASE_PATH%
set OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED=false

@echo on
%*
17 changes: 17 additions & 0 deletions src/Elastic.OpenTelemetry.AutoInstrumentation/_instrument.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/sh

BASE_PATH="$(cd "$(dirname "$0")" && pwd)"

# Settings for .NET
export ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=OpenTelemetry.AutoInstrumentation.AspNetCoreBootstrapper
export CORECLR_ENABLE_PROFILING=1
export CORECLR_PROFILER="{918728DD-259F-4A6A-AC2B-B85E1B658318}"
CORECLR_PROFILER_PATH="$(ls ${BASE_PATH}/OpenTelemetry.AutoInstrumentation.Native.*)"
export CORECLR_PROFILER_PATH
export DOTNET_STARTUP_HOOKS=${BASE_PATH}/OpenTelemetry.AutoInstrumentation.StartupHook.dll

# Settings for OpenTelemetry
export OTEL_DOTNET_AUTO_HOME=${BASE_PATH}
export OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED=false

exec "$@"
9 changes: 9 additions & 0 deletions src/Elastic.OpenTelemetry.AutoInstrumentation/instrument.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@echo off
setlocal

:: This script is expected to be used in a build that specified a RuntimeIdentifier (RID)
set BASE_PATH=%~dp0

set OTEL_DOTNET_AUTO_PLUGINS=Elastic.OpenTelemetry.AutoInstrumentationPlugin, Elastic.OpenTelemetry

call %BASE_PATH%_instrument.cmd %*
12 changes: 12 additions & 0 deletions src/Elastic.OpenTelemetry.AutoInstrumentation/instrument.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Hide the shipped content files VS project tree.
Can be removed once https://github.com/NuGet/Home/issues/4856 is resolved -->
<ItemGroup>
<Content Update="@(Content)">
<Visible Condition="'%(NuGetItemType)' == 'Content' and '%(NuGetPackageId)' == 'elastic.opentelemetry.autoinstrumentation'">False</Visible>
<CopyToOutputDirectory Condition="'%(NuGetItemType)' == 'Content' and '%(NuGetPackageId)' == 'elastic.opentelemetry.autoinstrumentation'">Always</CopyToOutputDirectory>
<CopyToPublishDirectory Condition="'%(NuGetItemType)' == 'Content' and '%(NuGetPackageId)' == 'elastic.opentelemetry.autoinstrumentation'">Always</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions src/Elastic.OpenTelemetry.AutoInstrumentation/instrument.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh

# This script is expected to be used in a build that specified a RuntimeIdentifier (RID)
BASE_PATH="$(cd "$(dirname "$0")" && pwd)"

export OTEL_DOTNET_AUTO_PLUGINS="Elastic.OpenTelemetry.AutoInstrumentationPlugin, Elastic.OpenTelemetry"

. $BASE_PATH/_instrument.sh

exec "$@"

0 comments on commit f842bb7

Please sign in to comment.