public static Secret CreateRegistrySecret(
            Output <string> @namespace,
            AutoDevOpsSettings.RegistrySettings registrySettings,
            ProviderResource?providerResource = null
            )
        {
            const string secretName = "gitlab-registry";

            var creds = $"{registrySettings.User}:{registrySettings.Password}".Base64Encode();

            var content =
                $"{{\"auths\":{{\"{registrySettings.Server}\":{{\"email\":\"{registrySettings.Email}\", \"auth\":\"{creds}\"}}}}}}"
                .Base64Encode();

            return(new Secret(
                       secretName,
                       new SecretArgs {
                Metadata = CreateArgs.GetMeta(secretName, @namespace),
                Type = "kubernetes.io/dockerconfigjson",
                Data = new InputMap <string> {
                    { ".dockerconfigjson", content }
                }
            },
                       new CustomResourceOptions {
                Provider = providerResource
            }
                       ));
        }
 public static PodMonitor CreatePodMonitor(
     AutoDevOpsSettings settings,
     Pulumi.Kubernetes.Apps.V1.Deployment deployment,
     Namespace kubeNamespace,
     ProviderResource?providerResource = null
     )
 => new(
        public static Ingress Create(
            Output <string> namespaceName,
            AutoDevOpsSettings settings,
            string ingressClass,
            Dictionary <string, string>?annotations = null,
            ProviderResource?providerResource       = null
            )
        {
            var ingressLabels = settings.BaseLabels();
            var tlsEnabled    = settings.Ingress.Tls?.Enabled == true;

            var ingressAnnotations = (annotations ?? new Dictionary <string, string>())
                                     .AsInputMap()
                                     .AddPair("kubernetes.io/ingress.class", ingressClass)
                                     .AddPairIf(tlsEnabled, "kubernetes.io/tls-acme", "true")
                                     .AddPairIf(
                settings.Prometheus.Metrics && ingressClass.Contains("nginx"),
                "nginx.ingress.kubernetes.io/server-snippet",
                "location /metrics { deny all; }"
                );

            var uri = new Uri(settings.Deploy.Url);

            var ingress = new IngressArgs {
                Metadata = CreateArgs.GetMeta(settings.FullName(), namespaceName, ingressAnnotations, ingressLabels),
                Spec     = new IngressSpecArgs {
                    Rules = CreateArgs.IngressRule(uri.Host, settings.FullName(), settings.Service.ExternalPort),
                    Tls   = tlsEnabled
                        ? new[] {
Exemple #4
0
        internal static async Task <string?> RegisterAsync(ProviderResource?provider)
        {
            if (provider == null)
            {
                return(null);
            }

            if (provider._registrationId == null)
            {
                var providerUrn = await provider.Urn.GetValueAsync(whenUnknown : default !).ConfigureAwait(false);
Exemple #5
0
        public static Deployment Create(
            Output <string> namespaceName,
            AutoDevOpsSettings settings,
            int replicas,
            Secret?imagePullSecret = null,
            Secret?appSecret       = null,
            IEnumerable <ContainerArgs>?sidecars        = null,
            Action <ContainerArgs>?configureContainer   = null,
            Action <PodSpecArgs>?configurePod           = null,
            Action <DeploymentArgs>?configureDeployment = null,
            ProviderResource?providerResource           = null
            )
        {
            var appLabels         = settings.AppLabels();
            var gitLabAnnotations = settings.GitLabAnnotations();

            var containers = CreateArgs.GetAppContainers(
                settings.Application,
                settings.Deploy,
                settings.GitLab,
                appSecret,
                sidecars,
                configureContainer
                );

            var deployment = new DeploymentArgs {
                Metadata = CreateArgs.GetMeta(settings.FullName(), namespaceName, gitLabAnnotations, appLabels),
                Spec     = new DeploymentSpecArgs {
                    Selector = new LabelSelectorArgs {
                        MatchLabels = appLabels
                    },
                    Replicas = replicas,
                    Template = CreateArgs.GetPodTemplate(
                        namespaceName,
                        containers,
                        imagePullSecret,
                        appLabels,
                        gitLabAnnotations,
                        60,
                        configurePod
                        )
                }
            };

            configureDeployment?.Invoke(deployment);

            return(new Deployment(
                       settings.PulumiName("deployment"),
                       deployment,
                       new CustomResourceOptions {
                Provider = providerResource
            }
                       ));
        }
Exemple #6
0
        public static Service Create(
            Output <string> namespaceName,
            AutoDevOpsSettings settings,
            Pulumi.Kubernetes.Apps.V1.Deployment deployment,
            Dictionary <string, string>?annotations = null,
            Action <ServiceArgs>?configureService   = null,
            ProviderResource?providerResource       = null
            )
        {
            var selector = deployment.Spec.Apply(x => x.Selector.MatchLabels);

            return(Create(namespaceName, settings, selector, annotations, configureService, providerResource));
        }
        /// <summary>
        /// Create the application secret from CI variables, which are prefixed with K8S_SECRET_
        /// </summary>
        /// <param name="namespaceName">Namespace, where the secret should be created</param>
        /// <param name="settings">AutoDevOps settings</param>
        /// <param name="providerResource">Optional: customer Kubernetes provider</param>
        /// <returns></returns>
        public static Secret?CreateAppSecret(
            Output <string> namespaceName,
            AutoDevOpsSettings settings,
            ProviderResource?providerResource = null
            )
        {
            var env = GetEnvironmentVariables();

            var vars = new Dictionary <string, string>();

            foreach (DictionaryEntry entry in env)
            {
                var key = (string)entry.Key;

                if (key.StartsWith("K8S_SECRET_") && entry.Value != null)
                {
                    vars[key.Remove(0, 11)] = (string)entry.Value;
                }
            }

            if (settings.Env != null)
            {
                foreach (var(name, value) in settings.Env)
                {
                    vars[name] = value;
                }
            }

            if (vars.Count == 0)
            {
                return(null);
            }

            var secretName = $"{settings.Application.Name}-secret";

            return(new Secret(
                       secretName,
                       new SecretArgs {
                Metadata = CreateArgs.GetMeta(secretName, namespaceName),
                Type = "opaque",
                StringData = vars
            },
                       new CustomResourceOptions {
                Provider = providerResource
            }
                       ));
        }
Exemple #8
0
        internal static async Task <string?> RegisterAsync(ProviderResource?provider)
        {
            if (provider == null)
            {
                return(null);
            }

            if (provider._registrationId == null)
            {
                var providerURN = await provider.Urn.GetValueAsync().ConfigureAwait(false);

                var providerID = await provider.Id.GetValueAsync().ConfigureAwait(false);

                if (string.IsNullOrEmpty(providerID))
                {
                    providerID = Constants.UnknownValue;
                }

                provider._registrationId = $"{providerURN}::{providerID}";
            }

            return(provider._registrationId);
        }
Exemple #9
0
        static Service Create(
            Output <string> namespaceName,
            AutoDevOpsSettings settings,
            InputMap <string> selector,
            Dictionary <string, string>?annotations = null,
            Action <ServiceArgs>?configureService   = null,
            ProviderResource?providerResource       = null
            )
        {
            var serviceLabels = settings.BaseLabels();

            var serviceAnnotations = (annotations ?? new Dictionary <string, string>())
                                     .AsInputMap();

            if (settings.Prometheus.Metrics && !settings.Prometheus.Operator)
            {
                serviceAnnotations
                .AddPair("prometheus.io/scrape", "true")
                .AddPair("prometheus.io/path", settings.Prometheus.Path)
                .AddPair("prometheus.io/port", settings.Service.ExternalPort.ToString());
            }

            var serviceArgs =
                new ServiceArgs {
                Metadata =
                    CreateArgs.GetMeta(settings.FullName(), namespaceName, serviceAnnotations, serviceLabels),
                Spec = new ServiceSpecArgs {
                    Type  = settings.Service.Type,
                    Ports = new List <ServicePortArgs> {
                        new() {
                            Name       = "web",
                            Port       = settings.Service.ExternalPort,
                            TargetPort = settings.Application.Port,
                            Protocol   = "TCP"
                        }
                    },
Exemple #10
0
        /// <summary>
        /// Creates and registers a new resource object.  <paramref name="type"/> is the fully
        /// qualified type token and <paramref name="name"/> is the "name" part to use in creating a
        /// stable and globally unique URN for the object. dependsOn is an optional list of other
        /// resources that this resource depends on, controlling the order in which we perform
        /// resource operations.
        /// </summary>
        /// <param name="type">The type of the resource.</param>
        /// <param name="name">The unique name of the resource.</param>
        /// <param name="custom">True to indicate that this is a custom resource, managed by a plugin.</param>
        /// <param name="args">The arguments to use to populate the new resource.</param>
        /// <param name="options">A bag of options that control this resource's behavior.</param>
        /// <param name="remote">True if this is a remote component resource.</param>
        /// <param name="dependency">True if this is a synthetic resource used internally for dependency tracking.</param>
        private protected Resource(
            string type, string name, bool custom,
            ResourceArgs args, ResourceOptions options,
            bool remote = false, bool dependency = false)
        {
            if (dependency)
            {
                _type      = "";
                _name      = "";
                _protect   = false;
                _providers = ImmutableDictionary <string, ProviderResource> .Empty;
                return;
            }

            if (string.IsNullOrEmpty(type))
            {
                throw new ArgumentException("'type' cannot be null or empty.", nameof(type));
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("'name' cannot be null or empty.", nameof(name));
            }

            // Before anything else - if there are transformations registered, invoke them in order
            // to transform the properties and options assigned to this resource.
            var parent = type == Stack._rootPulumiStackTypeName
                ? null
                : (options.Parent ?? Deployment.InternalInstance.Stack);

            _type = type;
            _name = name;

            var transformations = ImmutableArray.CreateBuilder <ResourceTransformation>();

            transformations.AddRange(options.ResourceTransformations);
            if (parent != null)
            {
                transformations.AddRange(parent._transformations);
            }
            this._transformations = transformations.ToImmutable();

            foreach (var transformation in this._transformations)
            {
                var tres = transformation(new ResourceTransformationArgs(this, args, options));
                if (tres != null)
                {
                    if (tres.Value.Options.Parent != options.Parent)
                    {
                        // This is currently not allowed because the parent tree is needed to
                        // establish what transformation to apply in the first place, and to compute
                        // inheritance of other resource options in the Resource constructor before
                        // transformations are run (so modifying it here would only even partially
                        // take affect).  It's theoretically possible this restriction could be
                        // lifted in the future, but for now just disallow re-parenting resources in
                        // transformations to be safe.
                        throw new ArgumentException("Transformations cannot currently be used to change the 'parent' of a resource.");
                    }

                    args    = tres.Value.Args;
                    options = tres.Value.Options;
                }
            }

            // Make a shallow clone of options to ensure we don't modify the value passed in.
            options = options.Clone();
            var componentOpts = options as ComponentResourceOptions;
            var customOpts    = options as CustomResourceOptions;

            if (options.Provider != null &&
                componentOpts?.Providers.Count > 0)
            {
                throw new ResourceException("Do not supply both 'provider' and 'providers' options to a ComponentResource.", options.Parent);
            }

            // Check the parent type if one exists and fill in any default options.
            this._providers = ImmutableDictionary <string, ProviderResource> .Empty;

            if (options.Parent != null)
            {
                var parentResource = options.Parent;
                lock (parentResource.ChildResources)
                {
                    parentResource.ChildResources.Add(this);
                }

                options.Protect ??= options.Parent._protect;

                // Make a copy of the aliases array, and add to it any implicit aliases inherited from its parent
                options.Aliases = options.Aliases.ToList();
                foreach (var parentAlias in options.Parent._aliases)
                {
                    options.Aliases.Add(Pulumi.Urn.InheritedChildAlias(name, options.Parent.GetResourceName(), parentAlias, type));
                }

                this._providers = options.Parent._providers;
            }

            if (custom)
            {
                var provider = customOpts?.Provider;
                if (provider == null)
                {
                    if (options.Parent != null)
                    {
                        // If no provider was given, but we have a parent, then inherit the
                        // provider from our parent.

                        options.Provider = options.Parent.GetProvider(type);
                    }
                }
                else
                {
                    // If a provider was specified, add it to the providers map under this type's package so that
                    // any children of this resource inherit its provider.
                    var typeComponents = type.Split(":");
                    if (typeComponents.Length == 3)
                    {
                        var pkg = typeComponents[0];
                        this._providers = this._providers.SetItem(pkg, provider);
                    }
                }
            }
            else
            {
                // Note: we checked above that at most one of options.provider or options.providers
                // is set.

                // If options.provider is set, treat that as if we were given a array of provider
                // with that single value in it.  Otherwise, take the array of providers, convert it
                // to a map and combine with any providers we've already set from our parent.
                var providerList = options.Provider != null
                    ? new List <ProviderResource> {
                    options.Provider
                }
                    : componentOpts?.Providers;

                this._providers = this._providers.AddRange(ConvertToProvidersMap(providerList));
            }

            this._protect  = options.Protect == true;
            this._provider = custom ? options.Provider : null;
            this._version  = options.Version;

            // Collapse any 'Alias'es down to URNs. We have to wait until this point to do so
            // because we do not know the default 'name' and 'type' to apply until we are inside the
            // resource constructor.
            var aliases = ImmutableArray.CreateBuilder <Input <string> >();

            foreach (var alias in options.Aliases)
            {
                aliases.Add(CollapseAliasToUrn(alias, name, type, options.Parent));
            }
            this._aliases = aliases.ToImmutable();

            Deployment.InternalInstance.ReadOrRegisterResource(this, remote, urn => new DependencyResource(urn), args, options);
        }
Exemple #11
0
        /// <summary>
        /// The AutoDevOps resource constructor, which takes care of deploying the whole thing
        /// </summary>
        /// <param name="settings">AutoDevOps settings, <see cref="AutoDevOpsSettings"/></param>
        /// <param name="sidecars">Optional: collection of sidecar containers</param>
        /// <param name="configureContainer">Optional: custom application container configuration</param>
        /// <param name="configurePod">Optional: custom pod configuration</param>
        /// <param name="configureDeployment">Optional: custom deployment configuration</param>
        /// <param name="configureService">Optional: custom service configuration</param>
        /// <param name="serviceAnnotations">Optional: service annotations</param>
        /// <param name="ingressAnnotations">Optional: ingress annotations</param>
        /// <param name="namespaceAnnotations">Optional: namespace annotations</param>
        /// <param name="deployExtras">Optional: use this to deploy other resources to the environment namespace,
        /// before the application is deployed.</param>
        /// <param name="provider">Optional: custom Kubernetes resource provider</param>
        public AutoDevOps(
            AutoDevOpsSettings settings,
            IEnumerable <ContainerArgs>?sidecars             = null,
            Action <ContainerArgs>?configureContainer        = null,
            Action <PodSpecArgs>?configurePod                = null,
            Action <DeploymentArgs>?configureDeployment      = null,
            Action <ServiceArgs>?configureService            = null,
            Dictionary <string, string>?serviceAnnotations   = null,
            Dictionary <string, string>?ingressAnnotations   = null,
            Dictionary <string, string>?namespaceAnnotations = null,
            Action <Namespace>?deployExtras = null,
            ProviderResource?provider       = null
            )
        {
            var @namespace    = KubeNamespace.Create(settings.Deploy.Namespace, namespaceAnnotations, provider);
            var namespaceName = @namespace.Metadata.Apply(x => x.Name);

            deployExtras?.Invoke(@namespace);

            var imagePullSecret = settings.GitLab.Visibility != "public" && settings.Registry != null
                ? KubeSecret.CreateRegistrySecret(namespaceName, settings.Registry, provider)
                : null;

            var replicas = settings.Deploy.Percentage > 0 || settings.Deploy.Replicas == 0
                ? GetReplicas(settings.Application.Track, settings.Deploy.Percentage)
                : settings.Deploy.Replicas;

            if (replicas == 0)
            {
                DeploymentResult = new Result {
                    Namespace = @namespace
                };
                return;
            }

            var appSecret = KubeSecret.CreateAppSecret(namespaceName, settings, provider);

            var deployment = KubeDeployment.Create(
                namespaceName,
                settings,
                replicas,
                imagePullSecret,
                appSecret,
                sidecars,
                configureContainer,
                configurePod,
                configureDeployment,
                provider
                );

            var service = settings.Service.Enabled
                ? KubeService.Create(
                namespaceName,
                settings,
                deployment,
                serviceAnnotations,
                configureService,
                provider
                )
                : null;

            var ingress = settings.Ingress.Enabled && !settings.Deploy.Url.IsEmpty()
                ? KubeIngress.Create(namespaceName, settings, settings.Ingress.Class, ingressAnnotations, provider)
                : null;

            if (settings.Prometheus.Metrics && settings.Prometheus.Operator)
            {
                if (service != null)
                {
                    Prometheus.CreateServiceMonitor(settings, service, @namespace, provider);
                }
                else
                {
                    Prometheus.CreatePodMonitor(settings, deployment, @namespace, provider);
                }
            }

            DeploymentResult = new Result {
                Namespace  = @namespace,
                Deployment = deployment,
                Service    = service,
                Ingress    = ingress
            };
        }
Exemple #12
0
 public static RoleBinding AddJaeger(
     Namespace kubeNamespace,
     string jaegerNamespace            = "observability",
     ProviderResource?providerResource = null)
 => new(