Create image for Oryx CLI to use in GitHub Actions

This commit is contained in:
cormacpayne 2019-11-21 09:55:41 -08:00
Родитель 63efedc4f0
Коммит dbb5a95c2d
10 изменённых файлов: 260 добавлений и 45 удалений

Просмотреть файл

@ -11,6 +11,7 @@ declare -r RELEASE_TAG_NAME="${RELEASE_TAG_NAME:-$BUILD_NUMBER}"
declare -r BUILD_IMAGES_BUILD_CONTEXT_DIR="$__REPO_DIR/"
declare -r BUILD_IMAGES_DOCKERFILE="$__REPO_DIR/images/build/Dockerfile"
declare -r BUILD_IMAGES_SLIM_DOCKERFILE="$__REPO_DIR/images/build/slim.Dockerfile"
declare -r BUILD_IMAGES_CLI_DOCKERFILE="$__REPO_DIR/images/build/cli.Dockerfile"
declare -r PACK_IMAGE_DOCKERFILE="$__REPO_DIR/images/pack-builder/pack-runner.Dockerfile"
declare -r ORYXTESTS_BUILDIMAGE_DOCKERFILE="$__REPO_DIR/tests/images/build/Dockerfile"
declare -r ORYXTESTS_SLIM_BUILDIMAGE_DOCKERFILE="$__REPO_DIR/tests/images/build/slim.Dockerfile"
@ -33,11 +34,13 @@ declare -r PACK_BUILDER_IMAGE_NAME="pack-builder"
declare -r ORYXTESTS_BUILDIMAGE_REPO="oryxtests/build"
declare -r DEVBOX_BUILD_IMAGES_REPO="oryx/build"
declare -r DEVBOX_CLI_BUILD_IMAGE_REPO="oryx/cli"
declare -r DEVBOX_RUNTIME_IMAGES_REPO_PREFIX="oryx"
declare -r ACR_DEV_NAME="oryxdevmcr.azurecr.io"
declare -r ACR_PUBLIC_PREFIX="$ACR_DEV_NAME/public/oryx"
declare -r ACR_BUILD_IMAGES_REPO="$ACR_DEV_NAME/public/oryx/build"
declare -r ACR_CLI_BUILD_IMAGE_REPO="$ACR_DEV_NAME/public/oryx/cli"
declare -r ACR_RUNTIME_IMAGES_REPO="$ACR_PUBLIC_PREFIX"
declare -r ACR_PACK_IMAGE_REPO="$ACR_PUBLIC_PREFIX/$PACK_IMAGE_NAME"

Просмотреть файл

@ -151,6 +151,40 @@ buildDockerImage "$BUILD_IMAGES_DOCKERFILE" \
"$ORYXTESTS_BUILDIMAGE_REPO" \
"$DEVBOX_BUILD_IMAGES_REPO"
echo
echo "-------------Creating CLI image-------------------"
builtImageTag="$ACR_CLI_BUILD_IMAGE_REPO:latest"
docker build -t $builtImageTag \
--build-arg AGENTBUILD=$BUILD_SIGNED \
$BASE_TAG_BUILD_ARGS \
--build-arg AI_KEY=$APPLICATION_INSIGHTS_INSTRUMENTATION_KEY \
$ctxArgs \
-f "$BUILD_IMAGES_CLI_DOCKERFILE" \
.
echo
echo "$builtImageTag" >> $ACR_BUILD_IMAGES_ARTIFACTS_FILE
# Retag build image with build number tags
if [ "$AGENT_BUILD" == "true" ]
then
uniqueTag="$BUILD_DEFINITIONNAME.$RELEASE_TAG_NAME"
echo
echo "Retagging image '$builtImageTag' with ACR related tags..."
docker tag "$builtImageTag" "$ACR_CLI_BUILD_IMAGE_REPO:latest"
docker tag "$builtImageTag" "$ACR_CLI_BUILD_IMAGE_REPO:$uniqueTag"
# Write the list of images that were built to artifacts folder
echo
echo "Writing the list of build images built to artifacts folder..."
# Write image list to artifacts file
echo "$ACR_CLI_BUILD_IMAGE_REPO:$uniqueTag" >> $ACR_BUILD_IMAGES_ARTIFACTS_FILE
else
docker tag "$builtImageTag" "$DEVBOX_CLI_BUILD_IMAGE_REPO:latest"
fi
# Build buildpack images
# 'pack create-builder' is not supported on Windows
if [[ "$OSTYPE" == "linux-gnu" ]] || [[ "$OSTYPE" == "darwin"* ]]; then

Просмотреть файл

@ -0,0 +1,20 @@
FROM oryxdevmcr.azurecr.io/public/oryx/build:slim as build
FROM debian:stretch-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
# .NET Core dependencies
libc6 \
libgcc1 \
libgssapi-krb5-2 \
libicu57 \
liblttng-ust0 \
libssl1.0.2 \
libstdc++6 \
zlib1g \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /opt/oryx /opt/oryx
COPY --from=build /opt/buildscriptgen/ /opt/buildscriptgen/
RUN chmod a+x /opt/buildscriptgen/GenerateBuildScript
ENV PATH="$PATH:/opt/oryx"

Просмотреть файл

@ -3,9 +3,11 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Oryx.BuildScriptGenerator.DotNetCore;
using Microsoft.Oryx.BuildScriptGenerator.Exceptions;
using Microsoft.Oryx.BuildScriptGenerator.Node;
using Microsoft.Oryx.BuildScriptGenerator.Resources;
@ -20,8 +22,8 @@ namespace Microsoft.Oryx.BuildScriptGenerator
private readonly IDictionary<string, IList<string>> _slimPlatformVersions =
new Dictionary<string, IList<string>>()
{
{ "dotnetcore", new List<string>() { "2.1" } },
{ "node", new List<string>() { "8", "10", "12" } },
{ "dotnet", new List<string>() { "2.1" } },
{ "nodejs", new List<string>() { "8.16", "10.17", "12.13" } },
{ "python", new List<string>() { "3.7", "3.8" } },
};
@ -49,12 +51,13 @@ namespace Microsoft.Oryx.BuildScriptGenerator
var platform = platformAndVersion.Key;
var version = platformAndVersion.Value;
if (!_slimPlatformVersions.ContainsKey(platform.Name) ||
!_slimPlatformVersions[platform.Name].Any(v => version.StartsWith(v)))
(!_slimPlatformVersions[platform.Name].Any(v => version.StartsWith(v)) &&
!_slimPlatformVersions[platform.Name].Any(v => v.StartsWith(version))))
{
buildImageTag = "latest";
}
runImage = platform.Name == "dotnet" ? "dotnetcore" : platform.Name;
runImage = ConvertToRuntimeName(platform.Name);
runImageTag = GenerateRuntimeTag(version);
}
@ -102,5 +105,20 @@ namespace Microsoft.Oryx.BuildScriptGenerator
return $"{split[0]}.{split[1]}";
}
private string ConvertToRuntimeName(string platformName)
{
if (string.Equals(platformName, DotNetCoreConstants.LanguageName, StringComparison.OrdinalIgnoreCase))
{
platformName = "dotnetcore";
}
if (string.Equals(platformName, NodeConstants.NodeJsName, StringComparison.OrdinalIgnoreCase))
{
platformName = "node";
}
return platformName;
}
}
}

Просмотреть файл

@ -3,7 +3,9 @@
// Licensed under the MIT license.
// --------------------------------------------------------------------------------------------
using System;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Oryx.BuildScriptGenerator.DotNetCore;
using Microsoft.Oryx.BuildScriptGenerator.Exceptions;
using Microsoft.Oryx.BuildScriptGenerator.Node;
using Microsoft.Oryx.Tests.Common;
@ -36,13 +38,13 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests
}
[Theory]
[InlineData("dotnetcore", "2.0", "latest")]
[InlineData("dotnetcore", "2.1", "slim")]
[InlineData("dotnetcore", "3.0", "latest")]
[InlineData("node", "6", "latest")]
[InlineData("node", "8", "slim")]
[InlineData("node", "10", "slim")]
[InlineData("node", "12", "slim")]
[InlineData("dotnet", "2.0", "latest")]
[InlineData("dotnet", "2.1", "slim")]
[InlineData("dotnet", "3.0", "latest")]
[InlineData("nodejs", "6", "latest")]
[InlineData("nodejs", "8", "slim")]
[InlineData("nodejs", "10", "slim")]
[InlineData("nodejs", "12", "slim")]
[InlineData("php", "5.6", "latest")]
[InlineData("php", "7.3", "latest")]
[InlineData("python", "2.7", "latest")]
@ -71,18 +73,21 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests
Assert.NotNull(dockerfile);
Assert.NotEqual(string.Empty, dockerfile);
Assert.Contains(string.Format(_buildImageFormat, expectedBuildTag), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat, platformName, platformVersion), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat,
ConvertToRuntimeName(platformName),
platformVersion),
dockerfile);
Assert.False(detector.DetectInvoked);
}
[Theory]
[InlineData("dotnetcore", "2.0", "latest")]
[InlineData("dotnetcore", "2.1", "slim")]
[InlineData("dotnetcore", "3.0", "latest")]
[InlineData("node", "6", "latest")]
[InlineData("node", "8", "slim")]
[InlineData("node", "10", "slim")]
[InlineData("node", "12", "slim")]
[InlineData("dotnet", "2.0", "latest")]
[InlineData("dotnet", "2.1", "slim")]
[InlineData("dotnet", "3.0", "latest")]
[InlineData("nodejs", "6", "latest")]
[InlineData("nodejs", "8", "slim")]
[InlineData("nodejs", "10", "slim")]
[InlineData("nodejs", "12", "slim")]
[InlineData("php", "5.6", "latest")]
[InlineData("php", "7.3", "latest")]
[InlineData("python", "2.7", "latest")]
@ -111,18 +116,21 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests
Assert.NotNull(dockerfile);
Assert.NotEqual(string.Empty, dockerfile);
Assert.Contains(string.Format(_buildImageFormat, expectedBuildTag), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat, platformName, detectedPlatformVersion), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat,
ConvertToRuntimeName(platformName),
detectedPlatformVersion),
dockerfile);
Assert.True(detector.DetectInvoked);
}
[Theory]
[InlineData("dotnetcore", "2.0", "latest")]
[InlineData("dotnetcore", "2.1", "slim")]
[InlineData("dotnetcore", "3.0", "latest")]
[InlineData("node", "6", "latest")]
[InlineData("node", "8", "slim")]
[InlineData("node", "10", "slim")]
[InlineData("node", "12", "slim")]
[InlineData("dotnet", "2.0", "latest")]
[InlineData("dotnet", "2.1", "slim")]
[InlineData("dotnet", "3.0", "latest")]
[InlineData("nodejs", "6", "latest")]
[InlineData("nodejs", "8", "slim")]
[InlineData("nodejs", "10", "slim")]
[InlineData("nodejs", "12", "slim")]
[InlineData("php", "5.6", "latest")]
[InlineData("php", "7.3", "latest")]
[InlineData("python", "2.7", "latest")]
@ -151,20 +159,23 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests
Assert.NotNull(dockerfile);
Assert.NotEqual(string.Empty, dockerfile);
Assert.Contains(string.Format(_buildImageFormat, expectedBuildTag), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat, detectedPlatformName, detectedPlatformVersion), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat,
ConvertToRuntimeName(detectedPlatformName),
detectedPlatformVersion),
dockerfile);
Assert.True(detector.DetectInvoked);
}
[Theory]
[InlineData("node", "8", "dotnetcore", "2.1", "slim")]
[InlineData("node", "8", "dotnetcore", "3.0", "latest")]
[InlineData("node", "12", "dotnetcore", "2.1", "slim")]
[InlineData("node", "12", "dotnetcore", "3.0", "latest")]
[InlineData("node", "8", "python", "3.7", "slim")]
[InlineData("node", "8", "python", "2.7", "latest")]
[InlineData("python", "3.7", "dotnetcore", "2.1", "slim")]
[InlineData("python", "3.7", "dotnetcore", "3.0", "latest")]
[InlineData("dotnetcore", "2.1", "php", "5.6", "latest")]
[InlineData("nodejs", "8.16", "dotnet", "2.1", "slim")]
[InlineData("nodejs", "8.16", "dotnet", "3.0", "latest")]
[InlineData("nodejs", "12.12", "dotnet", "2.1", "latest")]
[InlineData("nodejs", "12.12", "dotnet", "3.0", "latest")]
[InlineData("nodejs", "8.16", "python", "3.7", "slim")]
[InlineData("nodejs", "8.16", "python", "2.7", "latest")]
[InlineData("python", "3.7", "dotnet", "2.1", "slim")]
[InlineData("python", "3.7", "dotnet", "3.0", "latest")]
[InlineData("dotnet", "2.1", "php", "5.6", "latest")]
public void GenerateDockerfile_GeneratesBuildTagAndRuntime_ForMultiPlatformBuild(
string platformName,
string platformVersion,
@ -199,7 +210,10 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests
Assert.NotNull(dockerfile);
Assert.NotEqual(string.Empty, dockerfile);
Assert.Contains(string.Format(_buildImageFormat, expectedBuildTag), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat, runtimePlatformName, runtimePlatformVersion), dockerfile);
Assert.Contains(string.Format(_argRuntimeFormat,
ConvertToRuntimeName(runtimePlatformName),
runtimePlatformVersion),
dockerfile);
}
private DockerfileContext CreateDockerfileContext(
@ -231,5 +245,20 @@ namespace Microsoft.Oryx.BuildScriptGenerator.Tests
{
return new TestLanguageDetectorUsingLangName(name, version);
}
private string ConvertToRuntimeName(string platformName)
{
if (string.Equals(platformName, DotNetCoreConstants.LanguageName, StringComparison.OrdinalIgnoreCase))
{
platformName = "dotnetcore";
}
if (string.Equals(platformName, NodeConstants.NodeJsName, StringComparison.OrdinalIgnoreCase))
{
platformName = "node";
}
return platformName;
}
}
}

Просмотреть файл

@ -9,6 +9,8 @@ using Microsoft.Oryx.BuildScriptGenerator.Node;
using Microsoft.Oryx.BuildScriptGenerator.Php;
using Microsoft.Oryx.Common;
using Microsoft.Oryx.Tests.Common;
using Microsoft.Oryx.BuildScriptGenerator.DotNetCore;
using System;
namespace Microsoft.Oryx.BuildImage.Tests
{
@ -17,7 +19,7 @@ namespace Microsoft.Oryx.BuildImage.Tests
public OryxCommandTest(ITestOutputHelper output) : base(output) { }
[Fact]
public void Build_UsesCwd_WhenNoSourceDirGiven()
public void BuildImage_Build_UsesCwd_WhenNoSourceDirGiven()
{
// Act
var result = _dockerCli.Run(new DockerRunArguments
@ -39,7 +41,7 @@ namespace Microsoft.Oryx.BuildImage.Tests
}
[Fact]
public void CanExec_WithNoUsableToolsDetected()
public void BuildImage_CanExec_WithNoUsableToolsDetected()
{
// Arrange
var appPath = "/tmp";
@ -63,7 +65,7 @@ namespace Microsoft.Oryx.BuildImage.Tests
}
[Fact]
public void CanExec_SingleCommand()
public void BuildImage_CanExec_SingleCommand()
{
// Arrange
var appPath = "/tmp";
@ -91,7 +93,7 @@ namespace Microsoft.Oryx.BuildImage.Tests
}
[Fact]
public void CanExec_CommandInSourceDir()
public void BuildImage_CanExec_CommandInSourceDir()
{
// Arrange
var appPath = "/tmp";
@ -120,7 +122,7 @@ namespace Microsoft.Oryx.BuildImage.Tests
}
[Fact]
public void CanExec_MultipleCommands_WithOlderToolVersions()
public void BuildImage_CanExec_MultipleCommands_WithOlderToolVersions()
{
// Arrange
var appPath = "/tmp";
@ -155,7 +157,7 @@ namespace Microsoft.Oryx.BuildImage.Tests
}
[Fact]
public void Exec_PropagatesFailures()
public void BuildImage_Exec_PropagatesFailures()
{
// Arrange
var appPath = "/tmp";
@ -179,5 +181,50 @@ namespace Microsoft.Oryx.BuildImage.Tests
},
result.GetDebugInfo());
}
[Fact]
public void CliImage_Dockerfile_SucceedsWithBasicNodeApp()
{
// Arrange
var appPath = "/tmp";
var platformName = "nodejs";
var runtimeName = ConvertToRuntimeName(platformName);
var platformVersion = "10.17";
var repositoryName = "build";
var tagName = "slim";
var script = new ShellScriptBuilder()
.CreateFile($"{appPath}/{NodeConstants.PackageJsonFileName}", "{}")
.AddCommand($"oryx dockerfile {appPath} --platform {platformName} --platform-version {platformVersion}")
.ToString();
// Act
var result = _dockerCli.Run(_imageHelper.GetTestCliImage(), "/bin/bash", "-c", script);
// Assert
RunAsserts(
() =>
{
Assert.True(result.IsSuccess);
Assert.Contains($"{runtimeName}:{platformVersion}", result.StdOut);
Assert.Contains($"{repositoryName}:{tagName}", result.StdOut);
},
result.GetDebugInfo());
}
private string ConvertToRuntimeName(string platformName)
{
if (string.Equals(platformName, DotNetCoreConstants.LanguageName, StringComparison.OrdinalIgnoreCase))
{
platformName = "dotnetcore";
}
if (string.Equals(platformName, NodeConstants.NodeJsName, StringComparison.OrdinalIgnoreCase))
{
platformName = "node";
}
return platformName;
}
}
}

Просмотреть файл

@ -19,6 +19,7 @@ namespace Microsoft.Oryx.Tests.Common
private const string _buildRepository = "build";
private const string _packRepository = "pack";
private const string _cliRepository = "cli";
private const string _latestTag = "latest";
private const string _slimTag = "slim";
@ -146,6 +147,18 @@ namespace Microsoft.Oryx.Tests.Common
return $"{_image}/{_packRepository}:{tag}";
}
/// <summary>
/// Constructs a 'cli' image using either the default image base (oryxdevmcr.azurecr.io/public/oryx), or the
/// base set by the ORYX_TEST_IMAGE_BASE environment variable. If a tag suffix was set with the environment
/// variable ORYX_TEST_TAG_SUFFIX, it will be used as the tag, otherwise, the 'latest' tag will be used.
/// </summary>
/// <returns>A 'cli' image that can be pulled for testing.</returns>
public string GetTestCliImage()
{
var tag = GetTestTag();
return $"{_image}/{_cliRepository}:{tag}";
}
private string GetTestTag()
{
if (string.IsNullOrEmpty(_tagSuffix))

Просмотреть файл

@ -59,6 +59,13 @@ steps:
scriptPath: ./vsts/scripts/tagBuildpacksImagesForRelease.sh
condition: and(succeeded(), eq(variables['ReleaseBuildImages'], 'true'))
- task: ms-devlabs.utilitytasks.task-Shellpp.Shell++@0
displayName: 'Pull and create release tags for CLI images'
inputs:
type: FilePath
scriptPath: ./vsts/scripts/tagCliImagesForRelease.sh
condition: and(succeeded(), eq(variables['ReleaseBuildImages'], 'true'))
- task: ShellScript@2
displayName: 'Test runtime images'
inputs:
@ -90,6 +97,18 @@ steps:
enforceDockerNamingConvention: true
condition: and(succeeded(), eq(variables['ReleaseBuildImages'], 'true'))
- task: Docker@1
displayName: 'Push CLI images to ACR'
inputs:
azureSubscriptionEndpoint: ${{ parameters.ascName }}
azureContainerRegistry: ${{ parameters.acrProdName }}
command: 'Push an image'
pushMultipleImages: true
imageNamesPath: '$(Build.ArtifactStagingDirectory)/drop/images/cli-images-mcr.txt'
includeLatestTag: false
enforceDockerNamingConvention: true
condition: and(succeeded(), eq(variables['ReleaseBuildImages'], 'true'))
- task: Docker@1
displayName: 'Push runtime images to ACR'
inputs:

Просмотреть файл

@ -40,3 +40,4 @@ function tagBuildImage() {
tagBuildImage "oryxdevmcr.azurecr.io/public/oryx/build:Oryx-CI.$RELEASE_TAG_NAME" "latest" "$RELEASE_TAG_NAME"
tagBuildImage "oryxdevmcr.azurecr.io/public/oryx/build:slim-Oryx-CI.$RELEASE_TAG_NAME" "slim" "slim-$RELEASE_TAG_NAME"
tagBuildImage "oryxdevmcr.azurecr.io/public/oryx/cli:Oryx-CI.$RELEASE_TAG_NAME" "latest" "$RELEASE_TAG_NAME"

Просмотреть файл

@ -0,0 +1,31 @@
#!/bin/bash
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
# --------------------------------------------------------------------------------------------
set -o pipefail
declare -r REPO_DIR=$( cd $( dirname "$0" ) && cd .. && cd .. && pwd )
source $REPO_DIR/build/__variables.sh
declare -r outFile="$BUILD_ARTIFACTSTAGINGDIRECTORY/drop/images/cli-images-mcr.txt"
declare -r sourceImageRepo="oryxdevmcr.azurecr.io/public/oryx"
declare -r prodImageRepo="oryxmcr.azurecr.io/public/oryx"
sourceBranchName=$BUILD_SOURCEBRANCHNAME
cliImage="$sourceImageRepo/cli:Oryx-CI.$RELEASE_TAG_NAME"
echo "Pulling CLI image '$cliImage'..."
docker pull "$cliImage"
echo "Retagging CLI image with '$RELEASE_TAG_NAME'..."
echo "$prodImageRepo/cli:$RELEASE_TAG_NAME">>"$outFile"
docker tag "$cliImage" "$prodImageRepo/cli:$RELEASE_TAG_NAME"
if [ "$sourceBranchName" == "master" ]; then
echo "Retagging CLI image with 'stable'..."
docker tag "$cliImage" "$prodImageRepo/cli:stable"
echo "$prodImageRepo/cli:stable">>"$outFile"
else
echo "Not creating 'stable' or 'latest' tags as source branch is not 'master'. Current branch is $sourceBranchName"
fi