コード例 #1
0
        /// <summary>
        ///     Perform a JSON patch operation on a Kubernetes resource.
        /// </summary>
        /// <typeparam name="TResource">
        ///     The target resource type.
        /// </typeparam>
        /// <param name="patchAction">
        ///     A delegate that performs customisation of the patch operation.
        /// </param>
        /// <param name="request">
        ///     An <see cref="HttpRequest"/> representing the patch request.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <typeparamref name="TResource"/> representing the updated resource.
        /// </returns>
        protected async Task <TResource> PatchResourceRaw <TResource>(Action <JsonPatchDocument> patchAction, HttpRequest request, CancellationToken cancellationToken)
            where TResource : KubeResourceV1
        {
            if (patchAction == null)
            {
                throw new ArgumentNullException(nameof(patchAction));
            }

            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            // If possible, tell the consumer which resource type we had a problem with (helpful when all you find is the error message in the log).
            (string kind, string apiVersion) = KubeObjectV1.GetKubeKind <TResource>();

            var patch = new JsonPatchDocument();

            patchAction(patch);

            return(await
                   Http.PatchAsync(request,
                                   patchBody : patch,
                                   mediaType : PatchMediaType,
                                   cancellationToken : cancellationToken
                                   )
                   .ReadContentAsObjectV1Async <TResource>(
                       operationDescription: $"patch {apiVersion}/{kind} resource"
                       )
                   .ConfigureAwait(false));
        }
コード例 #2
0
        /// <summary>
        ///     Request deletion of the specified global (non-namespaced) resource.
        /// </summary>
        /// <typeparam name="TResource">
        ///     The type of resource to delete.
        /// </typeparam>
        /// <param name="resourceByNameNoNamespaceRequestTemplate">
        ///     The HTTP request template for addressing a non-namespaced <typeparamref name="TResource"/> by name.
        /// </param>
        /// <param name="name">
        ///     The name of the resource to delete.
        /// </param>
        /// <param name="propagationPolicy">
        ///     A <see cref="DeletePropagationPolicy"/> indicating how child resources should be deleted (if at all).
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <typeparamref name="TResource"/> representing the resource's most recent state before it was deleted, if <paramref name="propagationPolicy"/> is <see cref="DeletePropagationPolicy.Foreground"/>; otherwise, a <see cref="StatusV1"/> indicating the operation result.
        /// </returns>
        protected async Task <KubeResourceResultV1 <TResource> > DeleteGlobalResource <TResource>(HttpRequest resourceByNameNoNamespaceRequestTemplate, string name, DeletePropagationPolicy?propagationPolicy = null, CancellationToken cancellationToken = default)
            where TResource : KubeResourceV1
        {
            if (resourceByNameNoNamespaceRequestTemplate == null)
            {
                throw new ArgumentNullException(nameof(resourceByNameNoNamespaceRequestTemplate));
            }

            if (String.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'name'.", nameof(name));
            }

            var response = Http.DeleteAsJsonAsync(
                resourceByNameNoNamespaceRequestTemplate.WithTemplateParameters(new
            {
                Name = name
            }),
                deleteBody: new DeleteOptionsV1
            {
                PropagationPolicy = propagationPolicy
            },
                cancellationToken: cancellationToken
                );

            (string kind, string apiVersion) = KubeObjectV1.GetKubeKind <TResource>();
            string operationDescription = $"delete {apiVersion}/{kind} resource '{name}'";

            return(await response.ReadContentAsResourceOrStatusV1 <TResource>(operationDescription, HttpStatusCode.OK, HttpStatusCode.NotFound));
        }
コード例 #3
0
        public void KubeObjectV1_Kind_Success(Type kubeObjectType, string expectedKind, string expectedApiVersion)
        {
            KubeObjectV1 kubeObject = (KubeObjectV1)Activator.CreateInstance(kubeObjectType);

            Assert.Equal(expectedKind, kubeObject.Kind);
            Assert.Equal(expectedApiVersion, kubeObject.ApiVersion);
        }
コード例 #4
0
        /// <summary>
        ///     Retrieve metadata for a Kubernetes resource API.
        /// </summary>
        /// <param name="modelType">
        ///     The CLR <see cref="Type"/> of the model that represents the resource.
        /// </param>
        /// <returns>
        ///     The API metadata, or <c>null</c> if no metadata was found for the API.
        /// </returns>
        public KubeApiMetadata Get(Type modelType)
        {
            if (modelType == null)
            {
                throw new ArgumentNullException(nameof(modelType));
            }

            (string kind, string apiVersion) = KubeObjectV1.GetKubeKind(modelType);
            if (String.IsNullOrWhiteSpace(kind))
            {
                throw new ArgumentException($"Model type {modelType.FullName} has not been decorated with KubeResourceAttribute or KubeResourceListAttribute.", nameof(modelType));
            }

            return(Get(kind, apiVersion));
        }
コード例 #5
0
        public void KubeObject_JTokenType(Type kubeObjectType, JTokenType expectedTokenType)
        {
            KubeObjectV1 kubeObject = (KubeObjectV1)Activator.CreateInstance(kubeObjectType);

            JToken rootToken;

            using (JTokenWriter writer = new JTokenWriter())
            {
                new JsonSerializer().Serialize(writer, kubeObject);
                writer.Flush();

                rootToken = writer.Token;
            }

            Assert.NotNull(rootToken);
            Assert.Equal(rootToken.Type, expectedTokenType);
        }
コード例 #6
0
        /// <summary>
        ///     Read response content as either a <see cref="StatusV1"/> or a <typeparamref name="TResource"/> resource.<see cref="KubeObjectV1"/>.
        /// </summary>
        /// <typeparam name="TResource">
        ///     The expected resource type.
        /// </typeparam>
        /// <param name="response">
        ///     The HTTP response.
        /// </param>
        /// <param name="operationDescription">
        ///     A short description of the operation represented by the request (used in exception message if request was not successful).
        /// </param>
        /// <param name="successStatusCodes">
        ///     Optional <see cref="HttpStatusCode"/>s that should be treated as representing a successful response.
        /// </param>
        /// <returns>
        ///     The response content, as a <see cref="KubeObjectV1"/>.
        /// </returns>
        /// <exception cref="HttpRequestException{TResponse}">
        ///     The response status code was unexpected or did not represent success.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        ///     No formatters were configured for the request, or an appropriate formatter could not be found in the request's list of formatters.
        /// </exception>
        public static async Task <KubeResourceResultV1 <TResource> > ReadContentAsResourceOrStatusV1 <TResource>(this Task <HttpResponseMessage> response, string operationDescription, params HttpStatusCode[] successStatusCodes)
            where TResource : KubeResourceV1
        {
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            (string expectedKind, string expectedApiVersion) = KubeObjectV1.GetKubeKind <TResource>();

            HttpResponseMessage responseMessage = null;

            try
            {
                responseMessage = await response;

                JObject responseJson = await responseMessage.ReadContentAsAsync <JObject, StatusV1>(successStatusCodes);

                string actualKind = responseJson.Value <string>("kind");
                if (actualKind == null)
                {
                    throw new KubeClientException($"Unable to {operationDescription}: received an invalid response from the Kubernetes API (expected a resource, but response was missing 'kind' property).");
                }

                string actualApiVersion = responseJson.Value <string>("apiVersion");
                if (actualKind == null)
                {
                    throw new KubeClientException($"Unable to {operationDescription}: received an invalid response from the Kubernetes API (expected a resource, but response was missing 'apiVersion' property).");
                }

                JsonSerializer serializer = responseMessage.GetJsonSerializer();

                if ((actualKind, actualApiVersion) == (expectedKind, expectedApiVersion))
                {
                    return(serializer.Deserialize <TResource>(responseJson.CreateReader()));
                }
                else if ((actualKind, actualApiVersion) == ("Status", "v1"))
                {
                    return(serializer.Deserialize <StatusV1>(responseJson.CreateReader()));
                }
                else
                {
                    throw new KubeClientException($"Unable to {operationDescription}: received an unexpected response from the Kubernetes API (should be v1/Status or {expectedApiVersion}/{expectedKind}, but was {actualApiVersion}/{actualKind}).");
                }
            }
コード例 #7
0
        /// <summary>
        ///     Get a single resource, returning <c>null</c> if it does not exist.
        /// </summary>
        /// <typeparam name="TResource">
        ///     The type of resource to retrieve.
        /// </typeparam>
        /// <param name="request">
        ///     An <see cref="HttpRequest"/> representing the resource to retrieve.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <typeparamref name="TResource"/> representing the current state for the resource, or <c>null</c> if no resource was found with the specified name and namespace.
        /// </returns>
        protected async Task <TResource> GetSingleResource <TResource>(HttpRequest request, CancellationToken cancellationToken = default)
            where TResource : KubeResourceV1
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            using (HttpResponseMessage responseMessage = await Http.GetAsync(request, cancellationToken).ConfigureAwait(false))
            {
                if (responseMessage.IsSuccessStatusCode)
                {
                    return(await responseMessage.ReadContentAsAsync <TResource>().ConfigureAwait(false));
                }

                // Ensure that HttpStatusCode.NotFound actually refers to the target resource.
                StatusV1 status = await responseMessage.ReadContentAsStatusV1Async(HttpStatusCode.NotFound).ConfigureAwait(false);

                if (status.Reason == "NotFound")
                {
                    return(null);
                }

                // If possible, tell the consumer which resource type we had a problem with (helpful when all you find is the error message in the log).
                (string itemKind, string itemApiVersion) = KubeObjectV1.GetKubeKind <TResource>();
                string resourceTypeDescription =
                    !String.IsNullOrWhiteSpace(itemKind)
                        ? $"{itemKind} ({itemApiVersion}) resource"
                        : typeof(TResource).Name;

                throw new KubeApiException($"Unable to retrieve {resourceTypeDescription} (HTTP status {responseMessage.StatusCode}).",
                                           innerException: new HttpRequestException <StatusV1>(responseMessage.StatusCode,
                                                                                               response: await responseMessage.ReadContentAsStatusV1Async(responseMessage.StatusCode).ConfigureAwait(false)
                                                                                               )
                                           );
            }
        }