/// <summary> /// Checks whether an implementation directory matches the expected digest. /// Throws <see cref="DigestMismatchException"/> if it does not match. /// </summary> /// <param name="path">The path of the directory ot check.</param> /// <param name="manifestDigest">The expected digest.</param> /// <param name="handler">A callback object used when the the user is to be informed about progress.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="NotSupportedException"><paramref name="manifestDigest"/> does not list any supported digests.</exception> /// <exception cref="IOException">The directory could not be processed.</exception> /// <exception cref="UnauthorizedAccessException">Read access to the directory is not permitted.</exception> /// <exception cref="DigestMismatchException">The directory does not match the expected digest</exception> public static void Verify(string path, ManifestDigest manifestDigest, ITaskHandler handler) { #region Sanity checks if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } #endregion string expectedDigest = manifestDigest.Best ?? throw new NotSupportedException(Resources.NoKnownDigestMethod); var format = ManifestFormat.FromPrefix(expectedDigest); var builder = new ManifestBuilder(format); handler.RunTask(new ReadDirectory(path, builder)); if (Verify(builder.Manifest, expectedDigest) == null) { string manifestFilePath = Path.Combine(path, Manifest.ManifestFile); var expectedManifest = File.Exists(manifestFilePath) ? Manifest.Load(manifestFilePath, format) : null; throw new DigestMismatchException( expectedDigest, actualDigest: builder.Manifest.CalculateDigest(), expectedManifest, actualManifest: builder.Manifest); } }
public static string CalculateDigest(string path, ManifestFormat format, ITaskHandler handler) { var manifestGenerator = new ManifestGenerator(path, format); handler.RunTask(manifestGenerator); return(manifestGenerator.Manifest.CalculateDigest()); }
/** Instantiate with {@link #builder}. */ private BuildConfiguration( ImageConfiguration baseImageConfiguration, ImageConfiguration targetImageConfiguration, ImmutableHashSet <string> additionalTargetImageTags, ContainerConfiguration containerConfiguration, LayersCache baseImageLayersCache, LayersCache applicationLayersCache, ManifestFormat targetFormat, bool allowInsecureRegistries, bool offline, ImmutableArray <ILayerConfiguration> layerConfigurations, string toolName, string toolVersion, IEventHandlers eventHandlers) { this.baseImageConfiguration = baseImageConfiguration; this.targetImageConfiguration = targetImageConfiguration; this.additionalTargetImageTags = additionalTargetImageTags; this.containerConfiguration = containerConfiguration; this.baseImageLayersCache = baseImageLayersCache; this.applicationLayersCache = applicationLayersCache; this.targetFormat = targetFormat; this.allowInsecureRegistries = allowInsecureRegistries; this.offline = offline; this.layerConfigurations = layerConfigurations; this.toolName = toolName; this.toolVersion = toolVersion; this.eventHandlers = eventHandlers; }
private Image( ManifestFormat imageFormat, Instant created, string architecture, string os, ImageLayers layers, ImmutableArray <HistoryEntry> history, ImmutableDictionary <string, string> environment, ImmutableArray <string>?entrypoint, ImmutableArray <string>?programArguments, DockerHealthCheck healthCheck, ImmutableHashSet <Port> exposedPorts, ImmutableHashSet <AbsoluteUnixPath> volumes, ImmutableDictionary <string, string> labels, string workingDirectory, string user) { this.imageFormat = imageFormat; this.created = created; this.architecture = architecture; this.os = os; this.layers = layers; this.history = history; this.environment = environment; this.entrypoint = entrypoint; this.programArguments = programArguments; this.healthCheck = healthCheck; this.exposedPorts = exposedPorts; this.volumes = volumes; this.labels = labels; this.workingDirectory = workingDirectory; this.user = user; }
/** * Sets the target format of the container image. * * @param targetFormat the target format * @return this */ public Builder SetTargetFormat(ImageFormat targetFormat) { this.targetFormat = targetFormat == ImageFormat.Docker ? ManifestFormat.V22 : ManifestFormat.OCI; return(this); }
/// <inheritdoc/> public void Add(ManifestDigest manifestDigest, Action <IBuilder> build) { #region Sanity checks if (build == null) { throw new ArgumentNullException(nameof(build)); } #endregion if (manifestDigest.AvailableDigests.Any(digest => Directory.Exists(System.IO.Path.Combine(Path, digest)))) { throw new ImplementationAlreadyInStoreException(manifestDigest); } string expectedDigest = manifestDigest.Best ?? throw new NotSupportedException(Resources.NoKnownDigestMethod); Log.Debug($"Storing implementation {expectedDigest} in {this}"); var format = ManifestFormat.FromPrefix(manifestDigest.Best); // Place files in temp directory until digest is verified string tempDir = GetTempDir(); using var _ = new Disposable(() => DeleteTempDir(tempDir)); var builder = new ManifestBuilder(format); build(new DirectoryBuilder(tempDir, builder)); var manifest = ImplementationStoreUtils.Verify(builder.Manifest, expectedDigest); if (manifest == null) { throw new DigestMismatchException( expectedDigest, actualDigest: builder.Manifest.CalculateDigest(), actualManifest: builder.Manifest); } manifest.Save(System.IO.Path.Combine(tempDir, Manifest.ManifestFile)); string target = System.IO.Path.Combine(Path, expectedDigest); lock (_renameLock) // Prevent race-conditions when adding the same digest twice { if (Directory.Exists(target)) { throw new ImplementationAlreadyInStoreException(manifestDigest); } // Move directory to final destination try { Directory.Move(tempDir, target); } catch (IOException ex) when(ex.Message.Contains("already exists") || Directory.Exists(target)) { throw new ImplementationAlreadyInStoreException(manifestDigest); } } // Prevent any further changes to the directory if (UseWriteProtection) { EnableWriteProtection(target); } }
/// <summary> /// 解析文本 /// </summary> /// <param name="text"></param> public void Read(string text) { ManifestFormat format = new ManifestFormat(); format.Read(text); Root = new Node(); this.Parse(format.Root, Root); }
//private static DescriptorDigest fakeDigest = DescriptorDigest.fromHash(new string('a', 64)); private void SetUp(ManifestFormat imageFormat) { Image.Builder testImageBuilder = Image.CreateBuilder(imageFormat) .SetCreated(Instant.FromUnixTimeSeconds(20)) .SetArchitecture("wasm") .SetOs("js") .AddEnvironmentVariable("VAR1", "VAL1") .AddEnvironmentVariable("VAR2", "VAL2") .SetEntrypoint(new[] { "some", "entrypoint", "command" }) .SetProgramArguments(new[] { "arg1", "arg2" }) .SetHealthCheck( DockerHealthCheck.FromCommand(ImmutableArray.Create("CMD-SHELL", "/checkhealth")) .SetInterval(Duration.FromSeconds(3)) .SetTimeout(Duration.FromSeconds(1)) .SetStartPeriod(Duration.FromSeconds(2)) .SetRetries(3) .Build()) .AddExposedPorts(ImmutableHashSet.Create(Port.Tcp(1000), Port.Tcp(2000), Port.Udp(3000))) .AddVolumes( ImmutableHashSet.Create( AbsoluteUnixPath.Get("/var/job-result-data"), AbsoluteUnixPath.Get("/var/log/my-app-logs"))) .AddLabels(ImmutableDic.Of("key1", "value1", "key2", "value2")) .SetWorkingDirectory("/some/workspace") .SetUser("tomcat"); DescriptorDigest fakeDigest = DescriptorDigest.FromDigest( "sha256:8c662931926fa990b41da3c9f42663a537ccd498130030f9149173a0493832ad"); testImageBuilder.AddLayer( new FakeLayer(fakeDigest)); testImageBuilder.AddHistory( HistoryEntry.CreateBuilder() .SetCreationTimestamp(Instant.FromUnixTimeSeconds(0)) .SetAuthor("Bazel") .SetCreatedBy("bazel build ...") .SetEmptyLayer(true) .Build()); testImageBuilder.AddHistory( HistoryEntry.CreateBuilder() .SetCreationTimestamp(Instant.FromUnixTimeSeconds(20)) .SetAuthor("Fib") .SetCreatedBy("fib") .Build()); imageToJsonTranslator = new ImageToJsonTranslator(testImageBuilder.Build()); }
/** Tests translation of image to {@link BuildableManifestTemplate}. */ private async Task TestGetManifestAsync( ManifestFormat manifestTemplateClass, string translatedJsonFilename) { // Loads the expected JSON string. SystemPath jsonFile = Paths.Get(TestResources.GetResource(translatedJsonFilename).ToURI()); string expectedJson = Encoding.UTF8.GetString(Files.ReadAllBytes(jsonFile)); // Translates the image to the manifest and writes the JSON string. ContainerConfigurationTemplate containerConfiguration = imageToJsonTranslator.GetContainerConfiguration(); BlobDescriptor blobDescriptor = await Digests.ComputeJsonDescriptorAsync(containerConfiguration).ConfigureAwait(false); IBuildableManifestTemplate manifestTemplate = imageToJsonTranslator.GetManifestTemplate(manifestTemplateClass, blobDescriptor); Assert.AreEqual(expectedJson, JsonTemplateMapper.ToUtf8String(manifestTemplate)); }
/** * Gets a {@link BuildResult} from an {@link Image}. * * @param image the image * @param targetFormat the target format of the image * @return a new {@link BuildResult} with the image's digest and id * @throws IOException if writing the digest or container configuration fails */ public static async Task <BuildResult> FromImageAsync(Image image, ManifestFormat targetFormat) { ImageToJsonTranslator imageToJsonTranslator = new ImageToJsonTranslator(image); ContainerConfigurationTemplate configurationTemplate = imageToJsonTranslator.GetContainerConfiguration(); BlobDescriptor containerConfigurationBlobDescriptor = await Digests.ComputeJsonDescriptorAsync(configurationTemplate).ConfigureAwait(false); IBuildableManifestTemplate manifestTemplate = imageToJsonTranslator.GetManifestTemplate( targetFormat, containerConfigurationBlobDescriptor); DescriptorDigest imageDigest = await Digests.ComputeJsonDigestAsync(manifestTemplate).ConfigureAwait(false); DescriptorDigest imageId = containerConfigurationBlobDescriptor.GetDigest(); return(new BuildResult(imageDigest, imageId)); }
/** * Gets the manifest as a JSON template. The {@code containerConfigurationBlobDescriptor} must be * the {@link BlobDescriptor} obtained by writing out the container configuration JSON returned * from {@link #getContainerConfiguration()}. * * @param <T> child type of {@link BuildableManifestTemplate}. * @param manifestTemplateClass the JSON template to translate the image to. * @param containerConfigurationBlobDescriptor the container configuration descriptor. * @return the image contents serialized as JSON. */ public IBuildableManifestTemplate GetManifestTemplate( ManifestFormat manifestFormat, BlobDescriptor containerConfigurationBlobDescriptor) { containerConfigurationBlobDescriptor = containerConfigurationBlobDescriptor ?? throw new ArgumentNullException(nameof(containerConfigurationBlobDescriptor)); try { IBuildableManifestTemplate template; // ISet up the JSON template. switch (manifestFormat) { case ManifestFormat.V22: template = new V22ManifestTemplate(); break; case ManifestFormat.OCI: template = new OCIManifestTemplate(); break; default: throw new ArgumentOutOfRangeException(nameof(manifestFormat)); } IBuildableManifestTemplate buildableTemplate = template; // Adds the container configuration reference. DescriptorDigest containerConfigurationDigest = containerConfigurationBlobDescriptor.GetDigest(); long containerConfigurationSize = containerConfigurationBlobDescriptor.GetSize(); buildableTemplate.SetContainerConfiguration(containerConfigurationSize, containerConfigurationDigest); // Adds the layers. foreach (ILayer layer in image.GetLayers()) { buildableTemplate.AddLayer( layer.GetBlobDescriptor().GetSize(), layer.GetBlobDescriptor().GetDigest()); } // Serializes into JSON. return(template); } catch (JsonException ex) { throw new ArgumentException(manifestFormat + " cannot be instantiated", ex); } }
/// <inheritdoc/> public Digest([NotNull] ICommandHandler handler) : base(handler) { Options.Add("manifest", () => Resources.OptionManifest, _ => _printManifest = true); Options.Add("digest", () => Resources.OptionDigest, _ => _printDigest = true); Options.Add("algorithm=", () => Resources.OptionAlgorithm + Environment.NewLine + SupportedValues(ManifestFormat.All), delegate(string algorithm) { try { _algorithm = ManifestFormat.FromPrefix(algorithm); } #region Error handling catch (ArgumentException ex) { // Wrap exception since only certain exception types are allowed throw new OptionException(ex.Message, algorithm); } #endregion }); }
/// <summary> /// Creates a new implementation node. /// </summary> /// <param name="digest">The digest identifying the implementation.</param> /// <param name="store">The <see cref="IStore"/> the implementation is located in.</param> /// <exception cref="FormatException">The manifest file is not valid.</exception> /// <exception cref="IOException">The manifest file could not be read.</exception> /// <exception cref="UnauthorizedAccessException">Read access to the file is not permitted.</exception> protected ImplementationNode(ManifestDigest digest, [NotNull] IStore store) : base(store) { #region Sanity checks if (store == null) { throw new ArgumentNullException("store"); } #endregion _digest = digest; // Determine the total size of an implementation via its manifest file string path = store.GetPath(digest); if (path == null) { return; } string manifestPath = System.IO.Path.Combine(path, Manifest.ManifestFile); Size = Manifest.Load(manifestPath, ManifestFormat.FromPrefix(digest.AvailableDigests.FirstOrDefault())).TotalSize; }
/// <summary> /// Creates a new manifest builder. /// </summary> /// <param name="format">The manifest format.</param> public ManifestBuilder(ManifestFormat format) { Manifest = new(format); }
private sealed record DedupKey(long Size, long LastModified, ManifestFormat Format, string Digest);
public void TestBuilder() { const string expectedBaseImageServerUrl = "someserver"; const string expectedBaseImageName = "baseimage"; const string expectedBaseImageTag = "baseimagetag"; const string expectedTargetServerUrl = "someotherserver"; const string expectedTargetImageName = "targetimage"; const string expectedTargetTag = "targettag"; ISet <string> additionalTargetImageTags = ImmutableHashSet.Create("tag1", "tag2", "tag3"); ISet <string> expectedTargetImageTags = ImmutableHashSet.Create("targettag", "tag1", "tag2", "tag3"); IList <CredentialRetriever> credentialRetrievers = new List <CredentialRetriever> { () => Maybe.Of(Credential.From("username", "password")) }; Instant expectedCreationTime = Instant.FromUnixTimeSeconds(10000); IList <string> expectedEntrypoint = new[] { "some", "entrypoint" }; IList <string> expectedProgramArguments = new[] { "arg1", "arg2" }; IDictionary <string, string> expectedEnvironment = ImmutableDic.Of("key", "value"); ImmutableHashSet <Port> expectedExposedPorts = ImmutableHashSet.Create(Port.Tcp(1000), Port.Tcp(2000)); IDictionary <string, string> expectedLabels = ImmutableDic.Of("key1", "value1", "key2", "value2"); const ManifestFormat expectedTargetFormat = ManifestFormat.OCI; SystemPath expectedApplicationLayersCacheDirectory = Paths.Get("application/layers"); SystemPath expectedBaseImageLayersCacheDirectory = Paths.Get("base/image/layers"); IList <ILayerConfiguration> expectedLayerConfigurations = new List <ILayerConfiguration> { LayerConfiguration.CreateBuilder() .AddEntry(Paths.Get("sourceFile"), AbsoluteUnixPath.Get("/path/in/container")) .Build() }; const string expectedCreatedBy = "createdBy"; ImageConfiguration baseImageConfiguration = ImageConfiguration.CreateBuilder( ImageReference.Of( expectedBaseImageServerUrl, expectedBaseImageName, expectedBaseImageTag)) .Build(); ImageConfiguration targetImageConfiguration = ImageConfiguration.CreateBuilder( ImageReference.Of( expectedTargetServerUrl, expectedTargetImageName, expectedTargetTag)) .SetCredentialRetrievers(credentialRetrievers) .Build(); ContainerConfiguration containerConfiguration = ContainerConfiguration.CreateBuilder() .SetCreationTime(expectedCreationTime) .SetEntrypoint(expectedEntrypoint) .SetProgramArguments(expectedProgramArguments) .SetEnvironment(expectedEnvironment) .SetExposedPorts(expectedExposedPorts) .SetLabels(expectedLabels) .Build(); BuildConfiguration.Builder buildConfigurationBuilder = BuildConfiguration.CreateBuilder() .SetBaseImageConfiguration(baseImageConfiguration) .SetTargetImageConfiguration(targetImageConfiguration) .SetAdditionalTargetImageTags(additionalTargetImageTags) .SetContainerConfiguration(containerConfiguration) .SetApplicationLayersCacheDirectory(expectedApplicationLayersCacheDirectory) .SetBaseImageLayersCacheDirectory(expectedBaseImageLayersCacheDirectory) .SetTargetFormat(ImageFormat.OCI) .SetAllowInsecureRegistries(true) .SetLayerConfigurations(expectedLayerConfigurations) .SetToolName(expectedCreatedBy); BuildConfiguration buildConfiguration = buildConfigurationBuilder.Build(); Assert.IsNotNull(buildConfiguration.GetContainerConfiguration()); Assert.AreEqual( expectedCreationTime, buildConfiguration.GetContainerConfiguration().GetCreationTime()); Assert.AreEqual( expectedBaseImageServerUrl, buildConfiguration.GetBaseImageConfiguration().GetImageRegistry()); Assert.AreEqual( expectedBaseImageName, buildConfiguration.GetBaseImageConfiguration().GetImageRepository()); Assert.AreEqual( expectedBaseImageTag, buildConfiguration.GetBaseImageConfiguration().GetImageTag()); Assert.AreEqual( expectedTargetServerUrl, buildConfiguration.GetTargetImageConfiguration().GetImageRegistry()); Assert.AreEqual( expectedTargetImageName, buildConfiguration.GetTargetImageConfiguration().GetImageRepository()); Assert.AreEqual( expectedTargetTag, buildConfiguration.GetTargetImageConfiguration().GetImageTag()); Assert.AreEqual(expectedTargetImageTags, buildConfiguration.GetAllTargetImageTags()); Assert.AreEqual( Credential.From("username", "password"), buildConfiguration .GetTargetImageConfiguration() .GetCredentialRetrievers() [0] .Retrieve() .OrElseThrow(() => new AssertionException(""))); Assert.AreEqual( expectedProgramArguments, buildConfiguration.GetContainerConfiguration().GetProgramArguments()); Assert.AreEqual( expectedEnvironment, buildConfiguration.GetContainerConfiguration().GetEnvironmentMap()); Assert.AreEqual( expectedExposedPorts, buildConfiguration.GetContainerConfiguration().GetExposedPorts()); Assert.AreEqual(expectedLabels, buildConfiguration.GetContainerConfiguration().GetLabels()); Assert.AreEqual(expectedTargetFormat, buildConfiguration.GetTargetFormat()); Assert.AreEqual( expectedApplicationLayersCacheDirectory, buildConfigurationBuilder.GetApplicationLayersCacheDirectory()); Assert.AreEqual( expectedBaseImageLayersCacheDirectory, buildConfigurationBuilder.GetBaseImageLayersCacheDirectory()); Assert.IsTrue(buildConfiguration.GetAllowInsecureRegistries()); Assert.AreEqual(expectedLayerConfigurations, buildConfiguration.GetLayerConfigurations()); Assert.AreEqual( expectedEntrypoint, buildConfiguration.GetContainerConfiguration().GetEntrypoint()); Assert.AreEqual(expectedCreatedBy, buildConfiguration.GetToolName()); }
public void FromPrefix() { ManifestFormat.FromPrefix("sha1new=abc").Should().BeSameAs(ManifestFormat.Sha1New); ManifestFormat.FromPrefix("sha256=abc").Should().BeSameAs(ManifestFormat.Sha256); ManifestFormat.FromPrefix("sha256new_abc").Should().BeSameAs(ManifestFormat.Sha256New); }
public static Builder CreateBuilder(ManifestFormat imageFormat) { return(new Builder(imageFormat)); }
public Builder(ManifestFormat imageFormat) { this.imageFormat = imageFormat; }