Example #1
0
        /// <inheritdoc/>
        public override async Task AddAsync(DeveloperDisk disk, CancellationToken cancellationToken)
        {
            if (disk == null)
            {
                throw new ArgumentNullException(nameof(disk));
            }

            var factory = new DeveloperDiskRegistryImageFactory();

            (var manifest, var configStream, var layerStream) = await factory.CreateRegistryImageAsync(disk, cancellationToken).ConfigureAwait(false);

            using (layerStream)
                using (configStream)
                    using (Stream manifestStream = new MemoryStream())
                        using (var client = await this.clientFactory.CreateAsync(cancellationToken).ConfigureAwait(false))
                        {
                            // Send over the base layer
                            Descriptor layerDescriptor = await Descriptor.CreateAsync(layerStream, "application/vnd.oci.image.layer.nondistributable.v1.tar", cancellationToken);

                            await client.PushBlobAsync(this.repositoryName, layerStream, layerDescriptor.Digest, cancellationToken).ConfigureAwait(false);

                            // Prepare and send the configuration
                            var configDescriptor = await Descriptor.CreateAsync(configStream, "application/vnd.oci.image.config.v1+json", cancellationToken).ConfigureAwait(false);

                            await client.PushBlobAsync(this.repositoryName, configStream, configDescriptor.Digest, cancellationToken).ConfigureAwait(false);

                            // Prepare and send the manifest
                            manifestStream.Write(JsonSerializer.SerializeToUtf8Bytes(manifest));

                            await client.PushManifestAsync(this.repositoryName, disk.Version.ProductVersion.ToString(), manifestStream, cancellationToken);
                        }
        }
Example #2
0
        public void Dispose_Works()
        {
            var disk = new DeveloperDisk();

            disk.Dispose();
            Assert.True(disk.IsDisposed);

            disk       = new DeveloperDisk();
            disk.Image = new MemoryStream();
            disk.Dispose();

            Assert.Throws <ObjectDisposedException>(() => disk.Image.Length);
        }
Example #3
0
        private static async Task <Stream> CreateLayerAsync(DeveloperDisk developerDisk, CancellationToken cancellationToken)
        {
            MemoryStream stream = new MemoryStream();
            var          writer = new TarWriter(stream);

            var versionBytes = Encoding.UTF8.GetBytes(developerDisk.Version.ToDictionary().ToXmlPropertyList());

            var fileMode = LinuxFileMode.S_IFREG | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IROTH;

            await writer.AddFileAsync("SystemVersion.plist", fileMode, developerDisk.CreationTime, new MemoryStream(versionBytes), cancellationToken).ConfigureAwait(false);

            await writer.AddFileAsync("DeveloperDiskImage.dmg", fileMode, developerDisk.CreationTime, developerDisk.Image, cancellationToken).ConfigureAwait(false);

            await writer.AddFileAsync("DeveloperDiskImage.dmg.signature", fileMode, developerDisk.CreationTime, new MemoryStream(developerDisk.Signature), cancellationToken).ConfigureAwait(false);

            await writer.WriteTrailerAsync(cancellationToken).ConfigureAwait(false);

            stream.Seek(0, SeekOrigin.Begin);

            return(stream);
        }
Example #4
0
        public async Task AddAsync_Works_Async()
        {
            var disk = new DeveloperDisk()
            {
                Image     = new MemoryStream(Encoding.UTF8.GetBytes("Hello, world!")),
                Signature = new byte[] { 1, 2, 3, 4 },
                Version   = new SystemVersion()
                {
                    BuildID             = new Guid("5abf1921-e3e3-4bfc-94c5-6c6805f23815"),
                    ProductBuildVersion = new AppleVersion(1, 'A', 1),
                    ProductCopyright    = "Quamotion bv",
                    ProductName         = "Kaponata",
                    ProductVersion      = new Version(1, 0),
                },
                CreationTime = new DateTimeOffset(2000, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
            };

            var registryClient = new Mock <ImageRegistryClient>(MockBehavior.Strict);

            registryClient
            .Setup(c => c.PushBlobAsync("devimg", It.IsAny <Stream>(), "sha256:caad51da051d60541b2544a5984bdfc44ebceb894069007ad63b2ec073c911d0", default))
            .Returns(Task.FromResult(new Uri("http://localhost:5000/v2/devimg/blobs/sha256:caad51da051d60541b2544a5984bdfc44ebceb894069007ad63b2ec073c911d0")))
            .Verifiable();

            registryClient
            .Setup(c => c.PushBlobAsync("devimg", It.IsAny <Stream>(), "sha256:30c5c9a3aeb6eb237abf743b7657657580657e3548e82f6d2ce6e6e4660a9878", default))
            .Returns(Task.FromResult(new Uri("http://localhost:5000/v2/devimg/blobs/sha256:caad51da051d60541b2544a5984bdfc44ebceb894069007ad63b2ec073c911d0")))
            .Verifiable();

            registryClient
            .Setup(c => c.PushManifestAsync("devimg", "1.0", It.IsAny <Stream>(), default))
            .Returns(Task.FromResult(new Uri("http://localhost:5000/v2/devimg/manifests/1.0")))
            .Verifiable();

            var factory = new Mock <ImageRegistryClientFactory>(MockBehavior.Strict);

            factory.Setup(f => f.CreateAsync(default)).ReturnsAsync(registryClient.Object);
Example #5
0
        public async Task Create_Works_Async()
        {
            var disk = new DeveloperDisk()
            {
                Image     = new MemoryStream(Encoding.UTF8.GetBytes("Hello, world!")),
                Signature = new byte[] { 1, 2, 3, 4 },
                Version   = new SystemVersion()
                {
                    BuildID             = new Guid("5abf1921-e3e3-4bfc-94c5-6c6805f23815"),
                    ProductBuildVersion = new AppleVersion(1, 'A', 1),
                    ProductCopyright    = "Quamotion bv",
                    ProductName         = "Kaponata",
                    ProductVersion      = new Version(1, 0),
                },
                CreationTime = new DateTimeOffset(2000, 1, 1, 0, 0, 0, 0, TimeSpan.Zero),
            };

            var factory = new DeveloperDiskRegistryImageFactory();

            (var manifest, var configStream, var layerStream) = await factory.CreateRegistryImageAsync(disk, default).ConfigureAwait(false);

            Assert.NotNull(manifest);
            Assert.NotNull(configStream);
            Assert.Equal(0, configStream.Position);
            Assert.NotEqual(0, configStream.Length);

            Assert.NotNull(layerStream);
            Assert.Equal(0, layerStream.Position);
            Assert.NotEqual(0, layerStream.Length);

            var config = await JsonSerializer.DeserializeAsync <Image>(configStream, default).ConfigureAwait(false);

            // The RootFS is a Tar archive which contains the developer disk image, signature and a copy of the SystemVersion.plist file
            var reader = new TarReader(layerStream);

            (TarHeader? header, Stream entryStream) = await reader.ReadAsync(default).ConfigureAwait(false);
Example #6
0
        /// <summary>
        /// Asynchronously creates a registry image which contains a <see cref="DeveloperDisk"/>.
        /// </summary>
        /// <param name="developerDisk">
        /// The <see cref="DeveloperDisk"/> for which to create the image.
        /// </param>
        /// <param name="cancellationToken">
        /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
        /// </param>
        /// <returns>
        /// A <see cref="Task"/> which represents the asynchronous operation, and, when completed, returns the image manifest,
        /// image configuration and RootFS layer for the registry image containing the devleoper disk image.
        /// </returns>
        public async Task <(Manifest manifest, Stream configStream, Stream layer)> CreateRegistryImageAsync(DeveloperDisk developerDisk, CancellationToken cancellationToken)
        {
            if (developerDisk == null)
            {
                throw new ArgumentNullException(nameof(developerDisk));
            }

            var layer = await CreateLayerAsync(developerDisk, cancellationToken);

            var layerDescriptor = await Descriptor.CreateAsync(layer, "application/vnd.oci.image.layer.nondistributable.v1.tar", cancellationToken).ConfigureAwait(false);

            var    config       = CreateConfig(layerDescriptor.Digest);
            Stream configStream = new MemoryStream();
            await JsonSerializer.SerializeAsync(configStream, config, cancellationToken : cancellationToken).ConfigureAwait(false);

            configStream.Seek(0, SeekOrigin.Begin);

            var configDescriptor = await Descriptor.CreateAsync(configStream, "application/vnd.oci.image.config.v1+json", cancellationToken).ConfigureAwait(false);

            var manifest = CreateManifest(configDescriptor, layerDescriptor, developerDisk);

            return(manifest, configStream, layer);
        }
Example #7
0
 private static Manifest CreateManifest(Descriptor configDescriptor, Descriptor layerDescriptor, DeveloperDisk developerDisk)
 {
     return(new Manifest()
     {
         SchemaVersion = 2,
         Config = configDescriptor,
         Layers = new Descriptor[]
         {
             layerDescriptor,
         },
         Annotations = new Dictionary <string, string>()
         {
             { nameof(SystemVersion.BuildID), developerDisk.Version.BuildID !.ToString() },
Example #8
0
        /// <inheritdoc/>
        public override async Task <DeveloperDisk?> GetAsync(Version version, CancellationToken cancellationToken)
        {
            if (version == null)
            {
                throw new ArgumentNullException(nameof(version));
            }

            using (var client = await this.clientFactory.CreateAsync(cancellationToken).ConfigureAwait(false))
            {
                var manifest = await client.GetManifestAsync(this.repositoryName, version.ToString(), cancellationToken).ConfigureAwait(false);

                if (manifest == null)
                {
                    return(null);
                }

                // We should perform some sanity checks here.
                // Skip the config for now; although it is stored as a regular blob.
                // var config = await this.client.GetBlobAsync
                using (var layer = await client.GetBlobAsync(this.repositoryName, manifest.Layers[0].Digest, cancellationToken).ConfigureAwait(false))
                {
                    var disk = new DeveloperDisk();

                    TarReader reader = new TarReader(layer);
                    TarHeader?header;
                    Stream?   entryStream;

                    while (((header, entryStream) = await reader.ReadAsync(cancellationToken).ConfigureAwait(false)).header != null)
                    {
                        var fileName = header !.Value.FileName;

                        switch (fileName)
                        {
                        case "DeveloperDiskImage.dmg":
                            disk.Image        = new MemoryStream();
                            disk.CreationTime = header.Value.LastModified;
                            await entryStream !.CopyToAsync(disk.Image, cancellationToken).ConfigureAwait(false);
                            disk.Image.Seek(0, SeekOrigin.Begin);
                            break;

                        case "DeveloperDiskImage.dmg.signature":
                            disk.Signature = new byte[header.Value.FileSize];
                            await entryStream !.ReadBlockAsync(disk.Signature, cancellationToken).ConfigureAwait(false);
                            break;

                        case "SystemVersion.plist":
                            var   data = new byte[header.Value.FileSize];
                            await entryStream !.ReadBlockAsync(data, cancellationToken).ConfigureAwait(false);
                            var   plist = (NSDictionary)PropertyListParser.Parse(data);

                            disk.Version = new SystemVersion();
                            disk.Version.FromDictionary(plist);
                            break;

                        case "":
                            break;

                        default:
                            throw new InvalidDataException();
                        }
                    }

                    return(disk);
                }
            }
        }