Ejemplo n.º 1
0
        /**
         * Extract an {@link ErrorCodes} response from the error object encoded in an {@link
         * HttpResponseException}.
         *
         * @param httpResponseException the response exception
         * @return the parsed {@link ErrorCodes} if found
         * @throws HttpResponseException rethrows the original exception if an error object could not be
         *     parsed, if there were multiple error objects, or if the error code is unknown.
         */
        public static async Task <ErrorCode> GetErrorCodeAsync(HttpResponseMessage httpResponse)
        {
            httpResponse = httpResponse ?? throw new ArgumentNullException(nameof(httpResponse));
            // Obtain the error response code.
            string errorContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

            if (errorContent == null)
            {
                throw new HttpResponseException(httpResponse);
            }

            try
            {
                ErrorResponseTemplate errorResponse =
                    JsonTemplateMapper.ReadJson <ErrorResponseTemplate>(errorContent);
                IReadOnlyList <ErrorEntryTemplate> errors = errorResponse?.Errors;
                // There may be multiple error objects
                if (errors?.Count == 1)
                {
                    var errorCode = errors[0].Code;
                    // May not get an error code back.
                    if (errorCode.HasValue)
                    {
                        return(errorCode.GetValueOrDefault());
                    }
                }
            }
            catch (Exception e) when(e is IOException || e is ArgumentException)
            {
                // Parse exception: either isn't an error object or unknown error code
            }

            // rethrow the original exception
            throw new HttpResponseException(httpResponse);
        }
Ejemplo n.º 2
0
        protected override IManifestTemplate GetManifestTemplateFromJson(string jsonString)
        {
            var token = JToken.Parse(jsonString);

            if (!(token is JObject obj))
            {
                throw new UnknownManifestFormatException(Resources.ManifestPullerNotJsonExceptionMessage);
            }
            if (!obj.ContainsKey("schemaVersion"))
            {
                throw new UnknownManifestFormatException(Resources.ManifestPullerMissingSchemaVersionExceptionMessage);
            }
            if (!obj.TryGetValue("schemaVersion", out JToken schemaVersionToken) || schemaVersionToken.Type != JTokenType.Integer)
            {
                throw new UnknownManifestFormatException(Resources.ManifestPullerSchemaVersionNotIntExceptionMessage);
            }
            int schemaVersion = schemaVersionToken.Value <int>();

            if (schemaVersion == 1)
            {
                return(JsonTemplateMapper.ReadJson <V21ManifestTemplate>(jsonString));
            }
            if (schemaVersion == 2)
            {
                // 'schemaVersion' of 2 can be either Docker V2.2 or OCI.
                string mediaType = obj.Value <string>("mediaType");
                if (V22ManifestTemplate.ManifestMediaType == mediaType)
                {
                    return(JsonTemplateMapper.ReadJson <V22ManifestTemplate>(jsonString));
                }
                if (OCIManifestTemplate.ManifestMediaType == mediaType)
                {
                    return(JsonTemplateMapper.ReadJson <OCIManifestTemplate>(jsonString));
                }
                throw new UnknownManifestFormatException("Unknown mediaType: " + mediaType);
            }
            throw new UnknownManifestFormatException(
                      "Unknown schemaVersion: " + schemaVersion + " - only 1 and 2 are supported");
        }
Ejemplo n.º 3
0
        /**
         * Attempts to parse the container configuration JSON (of format {@code
         * application/vnd.docker.container.image.v1+json}) from the {@code v1Compatibility} value of the
         * first {@code history} entry, which corresponds to the latest layer.
         *
         * @return container configuration if the first history string holds it; {@code null} otherwise
         */
        public Maybe <ContainerConfigurationTemplate> GetContainerConfiguration()
        {
            try
            {
                if (History.Count == 0)
                {
                    return(Maybe.Empty <ContainerConfigurationTemplate>());
                }
                string v1Compatibility = History[0].V1Compatibility;
                if (v1Compatibility == null)
                {
                    return(Maybe.Empty <ContainerConfigurationTemplate>());
                }

                return(Maybe.Of(
                           JsonTemplateMapper.ReadJson <ContainerConfigurationTemplate>(v1Compatibility)));
            }
            catch (IOException)
            {
                // not a container configuration; ignore and continue
                return(Maybe.Empty <ContainerConfigurationTemplate>());
            }
        }
Ejemplo n.º 4
0
        /**
         * Pulls the base image.
         *
         * @param registryAuthorization authentication credentials to possibly use
         * @param progressEventDispatcher the {@link ProgressEventDispatcher} for emitting {@link
         *     ProgressEvent}s
         * @return the pulled image
         * @throws IOException when an I/O exception occurs during the pulling
         * @throws RegistryException if communicating with the registry caused a known error
         * @throws LayerCountMismatchException if the manifest and configuration contain conflicting layer
         *     information
         * @throws LayerPropertyNotFoundException if adding image layers fails
         * @throws BadContainerConfigurationFormatException if the container configuration is in a bad
         *     format
         */
        private async Task <Image> PullBaseImageAsync(
            Authorization registryAuthorization,
            ProgressEventDispatcher progressEventDispatcher)
        {
            RegistryClient registryClient =
                buildConfiguration
                .NewBaseImageRegistryClientFactory()
                .SetAuthorization(registryAuthorization)
                .NewRegistryClient();

            IManifestTemplate manifestTemplate =
                await registryClient.PullManifestAsync(buildConfiguration.GetBaseImageConfiguration().GetImageTag()).ConfigureAwait(false);

            // TODO: Make schema version be enum.
            switch (manifestTemplate.SchemaVersion)
            {
            case 1:
                V21ManifestTemplate v21ManifestTemplate = (V21ManifestTemplate)manifestTemplate;
                await buildConfiguration
                .GetBaseImageLayersCache()
                .WriteMetadataAsync(
                    buildConfiguration.GetBaseImageConfiguration().GetImage(), v21ManifestTemplate).ConfigureAwait(false);

                return(JsonToImageTranslator.ToImage(v21ManifestTemplate));

            case 2:
                IBuildableManifestTemplate buildableManifestTemplate =
                    (IBuildableManifestTemplate)manifestTemplate;
                if (buildableManifestTemplate.GetContainerConfiguration() == null ||
                    buildableManifestTemplate.GetContainerConfiguration().Digest == null)
                {
                    throw new UnknownManifestFormatException(
                              "Invalid container configuration in Docker V2.2/OCI manifest: \n"
                              + JsonTemplateMapper.ToUtf8String(buildableManifestTemplate));
                }

                DescriptorDigest containerConfigurationDigest =
                    buildableManifestTemplate.GetContainerConfiguration().Digest;

                using (ThrottledProgressEventDispatcherWrapper progressEventDispatcherWrapper =
                           new ThrottledProgressEventDispatcherWrapper(
                               progressEventDispatcher.NewChildProducer(),
                               "pull container configuration " + containerConfigurationDigest))
                {
                    string containerConfigurationString =
                        await Blobs.WriteToStringAsync(
                            registryClient.PullBlob(
                                containerConfigurationDigest,
                                progressEventDispatcherWrapper.SetProgressTarget,
                                progressEventDispatcherWrapper.DispatchProgress)).ConfigureAwait(false);

                    ContainerConfigurationTemplate containerConfigurationTemplate =
                        JsonTemplateMapper.ReadJson <ContainerConfigurationTemplate>(
                            containerConfigurationString);
                    await buildConfiguration
                    .GetBaseImageLayersCache()
                    .WriteMetadataAsync(
                        buildConfiguration.GetBaseImageConfiguration().GetImage(),
                        buildableManifestTemplate,
                        containerConfigurationTemplate).ConfigureAwait(false);

                    return(JsonToImageTranslator.ToImage(
                               buildableManifestTemplate, containerConfigurationTemplate));
                }
            }

            throw new InvalidOperationException(Resources.PullBaseImageStepUnknownManifestErrorMessage);
        }
Ejemplo n.º 5
0
        /**
         * Calls the credential helper CLI in the form:
         *
         * <pre>{@code
         * echo -n <server Uri> | docker-credential-<credential helper suffix> get
         * }</pre>
         *
         * @return the Docker credentials by calling the corresponding CLI
         * @throws IOException if writing/reading process input/output fails
         * @throws CredentialHelperUnhandledServerUrlException if no credentials could be found for the
         *     corresponding server
         * @throws CredentialHelperNotFoundException if the credential helper CLI doesn't exist
         */
        public Credential Retrieve()
        {
            try
            {
                IProcess process = new ProcessBuilder(credentialHelper, "get").Start();
                using (Stream processStdin = process.GetOutputStream())
                {
                    byte[] bytes = Encoding.UTF8.GetBytes(registry);
                    processStdin.Write(bytes, 0, bytes.Length);
                }

                using (StreamReader processStdoutReader =
                           new StreamReader(process.GetInputStream(), Encoding.UTF8))
                {
                    string output = processStdoutReader.ReadToEnd();

                    // Throws an exception if the credential store does not have credentials for serverUrl.
                    if (output.Contains("credentials not found in native keychain"))
                    {
                        throw new CredentialHelperUnhandledServerUrlException(
                                  credentialHelper, registry, output);
                    }
                    if (string.IsNullOrEmpty(output))
                    {
                        ThrowUnhandledUrlException(process);
                    }

                    try
                    {
                        DockerCredentialsTemplate dockerCredentials =
                            JsonTemplateMapper.ReadJson <DockerCredentialsTemplate>(output);
                        if (string.IsNullOrEmpty(dockerCredentials.Username) ||
                            string.IsNullOrEmpty(dockerCredentials.Secret))
                        {
                            throw new CredentialHelperUnhandledServerUrlException(
                                      credentialHelper, registry, output);
                        }

                        return(Credential.From(dockerCredentials.Username, dockerCredentials.Secret));
                    }
                    catch (JsonException ex)
                    {
                        throw new CredentialHelperUnhandledServerUrlException(
                                  credentialHelper, registry, output, ex);
                    }
                }
            }
            catch (Win32Exception ex) when(ex.NativeErrorCode == Win32ErrorCodes.FileNotFound)
            {
                if (ex.Message == null)
                {
                    throw;
                }

                // Checks if the failure is due to a nonexistent credential helper CLI.
                if (ex.Message.Contains("No such file or directory") ||
                    ex.Message.Contains("cannot find the file"))
                {
                    throw new CredentialHelperNotFoundException(credentialHelper, ex);
                }

                throw;
            }
        }
Ejemplo n.º 6
0
        /**
         * Sends the authentication request and retrieves the Bearer authorization token.
         *
         * @param credential the credential used to authenticate
         * @param scope the scope of permissions to authenticate for
         * @return the {@link Authorization} response
         * @throws RegistryAuthenticationFailedException if authentication fails
         * @see <a
         *     href="https://docs.docker.com/registry/spec/auth/token/#how-to-authenticate">https://docs.docker.com/registry/spec/auth/token/#how-to-authenticate</a>
         */
        private async Task <Authorization> AuthenticateAsync(Credential credential, string scope)
        {
            try
            {
                using (Connection connection =
                           Connection.GetConnectionFactory()(GetAuthenticationUrl(credential, scope)))
                    using (var request = new HttpRequestMessage())
                    {
                        foreach (var value in userAgent)
                        {
                            request.Headers.UserAgent.Add(value);
                        }

                        if (IsOAuth2Auth(credential))
                        {
                            string parameters = GetAuthRequestParameters(credential, scope);
                            request.Content = new BlobHttpContent(Blobs.From(parameters), MediaType.FormData);
                        }
                        else if (credential != null)
                        {
                            Authorization authorization = Authorization.FromBasicCredentials(credential.GetUsername(), credential.GetPassword());
                            request.Headers.Authorization = new AuthenticationHeaderValue(authorization.GetScheme(), authorization.GetToken());
                        }
                        if (IsOAuth2Auth(credential))
                        {
                            request.Method = HttpMethod.Post;
                        }
                        else
                        {
                            request.Method = HttpMethod.Get;
                        }

                        string responseString;
                        using (HttpResponseMessage response = await connection.SendAsync(request).ConfigureAwait(false))
                            using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), Encoding.UTF8))
                            {
                                responseString = await reader.ReadToEndAsync().ConfigureAwait(false);
                            }

                        AuthenticationResponseTemplate responseJson =
                            JsonTemplateMapper.ReadJson <AuthenticationResponseTemplate>(responseString);

                        if (responseJson.GetTokenOrAccessToken() == null)
                        {
                            throw new RegistryAuthenticationFailedException(
                                      registryEndpointRequestProperties.GetRegistry(),
                                      registryEndpointRequestProperties.GetImageName(),
                                      "Did not get token in authentication response from "
                                      + GetAuthenticationUrl(credential, scope)
                                      + "; parameters: "
                                      + GetAuthRequestParameters(credential, scope));
                        }
                        return(Authorization.FromBearerToken(responseJson.GetTokenOrAccessToken()));
                    }
            }
            catch (Exception ex) when(ex is IOException || ex is JsonException)
            {
                throw new RegistryAuthenticationFailedException(
                          registryEndpointRequestProperties.GetRegistry(),
                          registryEndpointRequestProperties.GetImageName(),
                          ex);
            }
        }
Ejemplo n.º 7
0
        public async Task TestWriteToAsync()
        {
            SystemPath fileA     = Paths.Get(TestResources.GetResource("core/fileA").ToURI());
            SystemPath fileB     = Paths.Get(TestResources.GetResource("core/fileB").ToURI());
            long       fileASize = Files.Size(fileA);
            long       fileBSize = Files.Size(fileB);

            DescriptorDigest fakeDigestA =
                DescriptorDigest.FromHash(
                    "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5");
            DescriptorDigest fakeDigestB =
                DescriptorDigest.FromHash(
                    "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc6");

            Mock.Get(mockLayer1).Setup(m => m.GetBlob()).Returns(Blobs.From(fileA));

            Mock.Get(mockLayer1).Setup(m => m.GetBlobDescriptor()).Returns(new BlobDescriptor(fileASize, fakeDigestA));

            Mock.Get(mockLayer1).Setup(m => m.GetDiffId()).Returns(fakeDigestA);

            Mock.Get(mockLayer2).Setup(m => m.GetBlob()).Returns(Blobs.From(fileB));

            Mock.Get(mockLayer2).Setup(m => m.GetBlobDescriptor()).Returns(new BlobDescriptor(fileBSize, fakeDigestB));

            Mock.Get(mockLayer2).Setup(m => m.GetDiffId()).Returns(fakeDigestB);

            Image testImage =
                Image.CreateBuilder(ManifestFormat.V22).AddLayer(mockLayer1).AddLayer(mockLayer2).Build();

            ImageTarball imageToTarball = new ImageTarball(testImage, ImageReference.Parse("my/image:tag"));

            MemoryStream @out = new MemoryStream();
            await imageToTarball.WriteToAsync(@out).ConfigureAwait(false);

            MemoryStream @in = new MemoryStream(@out.ToArray());

            using (TarInputStream tarArchiveInputStream = new TarInputStream(@in))
            {
                // Verifies layer with fileA was added.
                TarEntry headerFileALayer = tarArchiveInputStream.GetNextEntry();
                Assert.AreEqual(fakeDigestA.GetHash() + ".tar.gz", headerFileALayer.Name);
                string fileAString =
                    await new StreamReader(tarArchiveInputStream).ReadToEndAsync().ConfigureAwait(false);
                Assert.AreEqual(await Blobs.WriteToStringAsync(Blobs.From(fileA)).ConfigureAwait(false), fileAString);

                // Verifies layer with fileB was added.
                TarEntry headerFileBLayer = tarArchiveInputStream.GetNextEntry();
                Assert.AreEqual(fakeDigestB.GetHash() + ".tar.gz", headerFileBLayer.Name);
                string fileBString = await new StreamReader(tarArchiveInputStream).ReadToEndAsync().ConfigureAwait(false);
                Assert.AreEqual(await Blobs.WriteToStringAsync(Blobs.From(fileB)).ConfigureAwait(false), fileBString);

                // Verifies container configuration was added.
                TarEntry headerContainerConfiguration = tarArchiveInputStream.GetNextEntry();
                Assert.AreEqual("config.json", headerContainerConfiguration.Name);
                string containerConfigJson = await new StreamReader(tarArchiveInputStream).ReadToEndAsync().ConfigureAwait(false);
                JsonTemplateMapper.ReadJson <ContainerConfigurationTemplate>(containerConfigJson);

                // Verifies manifest was added.
                TarEntry headerManifest = tarArchiveInputStream.GetNextEntry();
                Assert.AreEqual("manifest.json", headerManifest.Name);
                string manifestJson = await new StreamReader(tarArchiveInputStream).ReadToEndAsync().ConfigureAwait(false);
                JsonTemplateMapper.ReadListOfJson <DockerLoadManifestEntryTemplate>(manifestJson);
            }
        }