From 421488d6f487e1261990745011260ed7e79c676b Mon Sep 17 00:00:00 2001 From: Eron Wright Date: Wed, 29 Nov 2023 22:23:15 -0800 Subject: [PATCH] dotnet sdk - option propagation --- .../gen/dotnet-templates/helm/ChartBase.cs | 18 +-- .../pkg/gen/dotnet-templates/helm/v3/Chart.cs | 14 +-- .../dotnet-templates/kustomize/Directory.cs | 26 ++-- .../gen/dotnet-templates/yaml/ConfigFile.cs | 32 ++--- .../gen/dotnet-templates/yaml/ConfigGroup.cs | 5 +- .../pkg/gen/dotnet-templates/yaml/yaml.tmpl | 114 ++++++++++------- sdk/dotnet/Helm/ChartBase.cs | 18 +-- sdk/dotnet/Helm/V3/Chart.cs | 14 +-- sdk/dotnet/Kustomize/Directory.cs | 26 ++-- sdk/dotnet/Yaml/ConfigFile.cs | 32 ++--- sdk/dotnet/Yaml/ConfigGroup.cs | 5 +- sdk/dotnet/Yaml/Yaml.cs | 116 +++++++++++------- 12 files changed, 213 insertions(+), 207 deletions(-) diff --git a/provider/pkg/gen/dotnet-templates/helm/ChartBase.cs b/provider/pkg/gen/dotnet-templates/helm/ChartBase.cs index 769700566c..5b044f1dbb 100644 --- a/provider/pkg/gen/dotnet-templates/helm/ChartBase.cs +++ b/provider/pkg/gen/dotnet-templates/helm/ChartBase.cs @@ -139,7 +139,7 @@ protected ChartBase(string releaseName, Union args, C var yaml = ExecuteCommand("helm", flags); return ParseTemplate( - yaml, cfgBase.Transformations, cfgBase.ResourcePrefix, dependencies, cfgBase.Namespace, options?.Provider); + yaml, cfgBase.Transformations, cfgBase.ResourcePrefix, dependencies, cfgBase.Namespace, options); } catch (Exception e) { @@ -261,20 +261,20 @@ private void Fetch(string chart, ChartFetchArgsUnwrap opts) private Output> ParseTemplate(string text, List transformations, string? resourcePrefix, ImmutableHashSet dependsOn, - string? defaultNamespace, Pu.ProviderResource provider) + string? defaultNamespace, ComponentResourceOptions? options) { + var childOpts = GetChildOptions(this, dependsOn.ToArray(), options); + var invokeOpts = GetInvokeOptions(childOpts); return Yaml.Invokes - .YamlDecode(new YamlDecodeArgs { Text = text, DefaultNamespace = defaultNamespace }, new InvokeOptions { Provider = provider }) + .YamlDecode(new YamlDecodeArgs { Text = text, DefaultNamespace = defaultNamespace }, invokeOpts) .Apply(objs => { - var args = new ConfigGroupArgs + return Parser.ParseYamlObjects(new ParseArgs { - ResourcePrefix = resourcePrefix, Objs = objs, - Transformations = transformations - }; - var opts = new ComponentResourceOptions { Parent = this, DependsOn = dependsOn.ToArray(), Provider = provider }; - return Parser.Parse(args, opts); + Transformations = transformations, + ResourcePrefix = resourcePrefix + }, childOpts); }); } diff --git a/provider/pkg/gen/dotnet-templates/helm/v3/Chart.cs b/provider/pkg/gen/dotnet-templates/helm/v3/Chart.cs index e9a52eace1..d521f2ab40 100644 --- a/provider/pkg/gen/dotnet-templates/helm/v3/Chart.cs +++ b/provider/pkg/gen/dotnet-templates/helm/v3/Chart.cs @@ -359,8 +359,10 @@ private Output> ParseTemplate(Un } jsonOptsString = JsonSerializer.Serialize(jsonOpts, serializeOptions); + var childOpts = GetChildOptions(this, dependsOn.ToArray(), options); + var invokeOpts = GetInvokeOptions(childOpts); return Invokes - .HelmTemplate(new HelmTemplateArgs { JsonOpts = jsonOptsString }, new InvokeOptions { Provider = options?.Provider }) + .HelmTemplate(new HelmTemplateArgs { JsonOpts = jsonOptsString }, invokeOpts) .Apply(objs => { var transformations = cfgBase.Transformations; @@ -368,14 +370,12 @@ private Output> ParseTemplate(Un { transformations = transformations.Append(Parser.SkipAwait).ToList(); } - var args = new ConfigGroupArgs + return Parser.ParseYamlObjects(new ParseArgs { - ResourcePrefix = cfgBase.ResourcePrefix, Objs = objs, - Transformations = transformations - }; - var opts = new ComponentResourceOptions { Parent = this, DependsOn = dependsOn.ToArray(), Provider = options?.Provider }; - return Parser.Parse(args, opts); + Transformations = transformations, + ResourcePrefix = cfgBase.ResourcePrefix + }, childOpts); }); } diff --git a/provider/pkg/gen/dotnet-templates/kustomize/Directory.cs b/provider/pkg/gen/dotnet-templates/kustomize/Directory.cs index b88522e147..5944ae7c32 100644 --- a/provider/pkg/gen/dotnet-templates/kustomize/Directory.cs +++ b/provider/pkg/gen/dotnet-templates/kustomize/Directory.cs @@ -141,27 +141,19 @@ public sealed class Directory : CollectionComponentResource public Directory(string name, DirectoryArgs args, ComponentResourceOptions? options = null) : base("kubernetes:kustomize:Directory", MakeName(args, name), options) { - name = GetName(args, name); - var objs = Invokes.KustomizeDirectory(new KustomizeDirectoryArgs { Directory = args.Directory }, new InvokeOptions { Provider = options?.Provider }); - var configGroupArgs = new ConfigGroupArgs - { - ResourcePrefix = args.ResourcePrefix, - Objs = objs, - Transformations = args.Transformations - }; - var opts = ComponentResourceOptions.Merge(options, new ComponentResourceOptions { Parent = this }); - var resources = Parser.Parse(configGroupArgs, opts); + var childOpts = GetChildOptions(this, null, options); + var invokeOpts = GetInvokeOptions(childOpts); + var objs = Invokes.KustomizeDirectory(new KustomizeDirectoryArgs { Directory = args.Directory }, invokeOpts); + var resources = Parser.ParseYamlObjects(new ParseArgs + { + Objs = objs, + Transformations = args.Transformations, + ResourcePrefix = args.ResourcePrefix + }, childOpts); RegisterResources(resources); } private static string MakeName(DirectoryArgs? args, string name) => args?.ResourcePrefix != null ? $"{args.ResourcePrefix}-{name}" : name; - - private static string GetName(DirectoryArgs config, string releaseName) - { - var prefix = config.ResourcePrefix; - return string.IsNullOrEmpty(prefix) ? releaseName : $"{prefix}-{releaseName}"; - } - } /// diff --git a/provider/pkg/gen/dotnet-templates/yaml/ConfigFile.cs b/provider/pkg/gen/dotnet-templates/yaml/ConfigFile.cs index eec1e671e4..a984608bc0 100644 --- a/provider/pkg/gen/dotnet-templates/yaml/ConfigFile.cs +++ b/provider/pkg/gen/dotnet-templates/yaml/ConfigFile.cs @@ -125,39 +125,25 @@ public ConfigFile(string name, ConfigFileArgs? args = null, ComponentResourceOpt : base("kubernetes:yaml:ConfigFile", MakeName(args, name), options) { name = MakeName(args, name); - options ??= new ComponentResourceOptions(); - options.Parent ??= this; - - var transformations = args?.Transformations ?? new List(); - if (args?.SkipAwait == true) - { - transformations.Add(Parser.SkipAwait); - } + var fileOutput = args?.File.ToOutput() ?? Output.Create(name); + var childOpts = GetChildOptions(this, null, options); var resources = fileOutput.Apply(fileId => { try { - if (Parser.IsUrl(fileId)) - { - using var wc = new System.Net.WebClient(); - return wc.DownloadString(fileId); - } - - return File.ReadAllText(fileId); + return Parser.Parse(new ConfigGroupArgs{ + Files = new string[]{ fileId }, + Transformations = args?.Transformations ?? new List(), + ResourcePrefix = args?.ResourcePrefix, + SkipAwait = args?.SkipAwait + }, false, childOpts); } catch (Exception e) { throw new ResourceException($"Error fetching YAML file '{fileId}': {e.Message}", this); } - }).Apply(text => - Parser.ParseYamlDocument(new ParseArgs - { - Objs = Invokes.YamlDecode(new YamlDecodeArgs { Text = text }, new InvokeOptions { Provider = options?.Provider }), - Transformations = transformations, - ResourcePrefix = args?.ResourcePrefix - }, options)); - + }); RegisterResources(resources); } diff --git a/provider/pkg/gen/dotnet-templates/yaml/ConfigGroup.cs b/provider/pkg/gen/dotnet-templates/yaml/ConfigGroup.cs index 8d55c260fd..ac7409f809 100644 --- a/provider/pkg/gen/dotnet-templates/yaml/ConfigGroup.cs +++ b/provider/pkg/gen/dotnet-templates/yaml/ConfigGroup.cs @@ -205,9 +205,8 @@ public sealed class ConfigGroup : CollectionComponentResource public ConfigGroup(string name, ConfigGroupArgs config, ComponentResourceOptions? options = null) : base("kubernetes:yaml:ConfigGroup", name, options) { - options ??= new ComponentResourceOptions(); - options.Parent ??= this; - RegisterResources(Parser.Parse(config, options)); + var childOpts = GetChildOptions(this, null, options); + RegisterResources(Parser.Parse(config, true, childOpts)); } } diff --git a/provider/pkg/gen/dotnet-templates/yaml/yaml.tmpl b/provider/pkg/gen/dotnet-templates/yaml/yaml.tmpl index 307bac8a22..f78b4a5145 100644 --- a/provider/pkg/gen/dotnet-templates/yaml/yaml.tmpl +++ b/provider/pkg/gen/dotnet-templates/yaml/yaml.tmpl @@ -100,61 +100,95 @@ namespace Pulumi.Kubernetes.Yaml var id = namespaceName != null ? $"{namespaceName}/{name}" : name; return Resources.Apply(r => (CustomResource)r[$"{groupVersionKind}::{id}"]); } + + protected static CustomResourceOptions GetChildOptions(Pu.Resource parent, InputList? extraDependsOn, ComponentResourceOptions? options) + { + // Create resource options based on component resource options. + var dependsOn = new InputList(); + if (options?.DependsOn is not null) + dependsOn.AddRange(options.DependsOn); + if (extraDependsOn is not null) + dependsOn.AddRange(extraDependsOn); + return new CustomResourceOptions + { + Parent = parent, + DependsOn = dependsOn, + }; + } + + protected internal static InvokeOptions GetInvokeOptions(CustomResourceOptions? options) + { + return new InvokeOptions { + Parent = options?.Parent, + Provider = options?.Provider, + Version = options?.Version, + PluginDownloadURL = options?.PluginDownloadURL, + }.WithDefaults(); + } } internal static class Parser { - public static Output> Parse(ConfigGroupArgs config, ComponentResourceOptions? options) + public static Output> Parse(ConfigGroupArgs config, bool glob, CustomResourceOptions? opts) { var resources = Output.Create(ImmutableDictionary.Create()); var transformations = config.Transformations; - if (config?.SkipAwait == true) + if (config.SkipAwait == true) { transformations.Add(SkipAwait); } + var yamls = new InputList(); + if (config.Yaml != null) + yamls.AddRange(config.Yaml); + if (config.Files != null) { - var files = new List(); foreach (var file in config.Files) { + // Read the raw YAML file(s) specified in the input file parameter. It might be a URL or a file path. if (IsUrl(file)) - files.Add(file); - else - files.AddRange(Glob.Files(Directory.GetCurrentDirectory(), file)); - } - - foreach (var file in files) - { - var cf = new ConfigFile( - file, - new ConfigFileArgs + { + // If the string looks like a URL, in that it begins with a scheme, fetch it over the network. + using var wc = new System.Net.WebClient(); + var yaml = wc.DownloadString(file); + yamls.Add(yaml); + } + else + { + // Otherwise, assume this is a path to a file on disk. If globbing is enabled, we might have + // multiple files -- otherwise just read a singular file. + var files = new List(); + if (glob) + files.AddRange(Glob.Files(Directory.GetCurrentDirectory(), file)); + else + files.Add(file); + foreach (var fileId in files) { - File = file, - Transformations = transformations, - ResourcePrefix = config.ResourcePrefix - }, - options); - resources = Output.Tuple(resources, cf.Resources).Apply(vs => vs.Item1.AddRange(vs.Item2)); + var yaml = File.ReadAllText(fileId); + yamls.Add(yaml); + } + } } } - if (config.Yaml != null) + if (yamls is not null) { - var yamlResources = config.Yaml.ToOutput().Apply(texts => + var invokeOpts = CollectionComponentResource.GetInvokeOptions(opts); + var yamlResources = yamls.ToOutput().Apply(texts => { - var yamls = texts + var r = texts .Select(text => - ParseYamlDocument(new ParseArgs + ParseYamlObjects(new ParseArgs { - Objs = Invokes.YamlDecode(new YamlDecodeArgs { Text = text }, new InvokeOptions { Provider = options?.Provider }), + Objs = Invokes.YamlDecode(new YamlDecodeArgs { Text = text }, invokeOpts), Transformations = transformations, - ResourcePrefix = config.ResourcePrefix - }, options)) + ResourcePrefix = config?.ResourcePrefix + }, opts)) .Select(output => (Input>)output) .ToImmutableArray(); - return Output.All(yamls); + return Output.All(r); }); resources = Output.Tuple(resources, yamlResources).Apply(vs => @@ -169,12 +203,12 @@ namespace Pulumi.Kubernetes.Yaml if (config.Objs != null) { - var docResources = ParseYamlDocument(new ParseArgs + var docResources = ParseYamlObjects(new ParseArgs { Objs = config.Objs, Transformations = transformations, ResourcePrefix = config.ResourcePrefix - }, options); + }, opts); resources = Output.Tuple(resources, docResources).Apply(vs => vs.Item1.AddRange(vs.Item2)); } @@ -206,14 +240,14 @@ namespace Pulumi.Kubernetes.Yaml internal static bool IsUrl(string s) => s.StartsWith("http://", StringComparison.Ordinal) || s.StartsWith("https://", StringComparison.Ordinal); - internal static Output> ParseYamlDocument(ParseArgs config, - ComponentResourceOptions? options = null) + internal static Output> ParseYamlObjects(ParseArgs config, + CustomResourceOptions? opts) { return config.Objs.ToOutput().Apply(objs => { var inputs = objs - .SelectMany(obj => ParseYamlObject(obj, config.Transformations, config.ResourcePrefix, options)) + .SelectMany(obj => ParseYamlObject(obj, config.Transformations, config.ResourcePrefix, opts)) .Select(output => (Input<(string, KubernetesResource)>) output) .ToImmutableArray(); @@ -225,21 +259,13 @@ namespace Pulumi.Kubernetes.Yaml } private static Output<(string, KubernetesResource)>[] ParseYamlObject(ImmutableDictionary obj, - List? transformations, string? resourcePrefix, ComponentResourceOptions? options = null) + List? transformations, string? resourcePrefix, CustomResourceOptions? opts) { if (obj == null || obj.Count == 0) return new Output<(string, KubernetesResource)>[0]; - // Create custom resource options based on component resource options. - var opts = new CustomResourceOptions - { - Parent = options?.Parent, - DependsOn = options?.DependsOn ?? new InputList(), - IgnoreChanges = options?.IgnoreChanges ?? new List(), - Version = options?.Version, - Provider = options?.Provider, - CustomTimeouts = options?.CustomTimeouts - }; + // Create a copy of opts to pass into potentially mutating transforms that will be applied to this resource. + opts = CustomResourceOptions.Merge(null, opts); // Allow users to change API objects before any validation. if (transformations != null) @@ -281,7 +307,7 @@ namespace Pulumi.Kubernetes.Yaml if (obj["items"] is IEnumerable> items) { foreach (var item in items) - objs.AddRange(Parser.ParseYamlObject(item, transformations, resourcePrefix)); + objs.AddRange(Parser.ParseYamlObject(item, transformations, resourcePrefix, opts)); } return objs.ToArray(); } diff --git a/sdk/dotnet/Helm/ChartBase.cs b/sdk/dotnet/Helm/ChartBase.cs index 769700566c..5b044f1dbb 100644 --- a/sdk/dotnet/Helm/ChartBase.cs +++ b/sdk/dotnet/Helm/ChartBase.cs @@ -139,7 +139,7 @@ protected ChartBase(string releaseName, Union args, C var yaml = ExecuteCommand("helm", flags); return ParseTemplate( - yaml, cfgBase.Transformations, cfgBase.ResourcePrefix, dependencies, cfgBase.Namespace, options?.Provider); + yaml, cfgBase.Transformations, cfgBase.ResourcePrefix, dependencies, cfgBase.Namespace, options); } catch (Exception e) { @@ -261,20 +261,20 @@ private void Fetch(string chart, ChartFetchArgsUnwrap opts) private Output> ParseTemplate(string text, List transformations, string? resourcePrefix, ImmutableHashSet dependsOn, - string? defaultNamespace, Pu.ProviderResource provider) + string? defaultNamespace, ComponentResourceOptions? options) { + var childOpts = GetChildOptions(this, dependsOn.ToArray(), options); + var invokeOpts = GetInvokeOptions(childOpts); return Yaml.Invokes - .YamlDecode(new YamlDecodeArgs { Text = text, DefaultNamespace = defaultNamespace }, new InvokeOptions { Provider = provider }) + .YamlDecode(new YamlDecodeArgs { Text = text, DefaultNamespace = defaultNamespace }, invokeOpts) .Apply(objs => { - var args = new ConfigGroupArgs + return Parser.ParseYamlObjects(new ParseArgs { - ResourcePrefix = resourcePrefix, Objs = objs, - Transformations = transformations - }; - var opts = new ComponentResourceOptions { Parent = this, DependsOn = dependsOn.ToArray(), Provider = provider }; - return Parser.Parse(args, opts); + Transformations = transformations, + ResourcePrefix = resourcePrefix + }, childOpts); }); } diff --git a/sdk/dotnet/Helm/V3/Chart.cs b/sdk/dotnet/Helm/V3/Chart.cs index e9a52eace1..d521f2ab40 100644 --- a/sdk/dotnet/Helm/V3/Chart.cs +++ b/sdk/dotnet/Helm/V3/Chart.cs @@ -359,8 +359,10 @@ private Output> ParseTemplate(Un } jsonOptsString = JsonSerializer.Serialize(jsonOpts, serializeOptions); + var childOpts = GetChildOptions(this, dependsOn.ToArray(), options); + var invokeOpts = GetInvokeOptions(childOpts); return Invokes - .HelmTemplate(new HelmTemplateArgs { JsonOpts = jsonOptsString }, new InvokeOptions { Provider = options?.Provider }) + .HelmTemplate(new HelmTemplateArgs { JsonOpts = jsonOptsString }, invokeOpts) .Apply(objs => { var transformations = cfgBase.Transformations; @@ -368,14 +370,12 @@ private Output> ParseTemplate(Un { transformations = transformations.Append(Parser.SkipAwait).ToList(); } - var args = new ConfigGroupArgs + return Parser.ParseYamlObjects(new ParseArgs { - ResourcePrefix = cfgBase.ResourcePrefix, Objs = objs, - Transformations = transformations - }; - var opts = new ComponentResourceOptions { Parent = this, DependsOn = dependsOn.ToArray(), Provider = options?.Provider }; - return Parser.Parse(args, opts); + Transformations = transformations, + ResourcePrefix = cfgBase.ResourcePrefix + }, childOpts); }); } diff --git a/sdk/dotnet/Kustomize/Directory.cs b/sdk/dotnet/Kustomize/Directory.cs index b88522e147..5944ae7c32 100644 --- a/sdk/dotnet/Kustomize/Directory.cs +++ b/sdk/dotnet/Kustomize/Directory.cs @@ -141,27 +141,19 @@ public sealed class Directory : CollectionComponentResource public Directory(string name, DirectoryArgs args, ComponentResourceOptions? options = null) : base("kubernetes:kustomize:Directory", MakeName(args, name), options) { - name = GetName(args, name); - var objs = Invokes.KustomizeDirectory(new KustomizeDirectoryArgs { Directory = args.Directory }, new InvokeOptions { Provider = options?.Provider }); - var configGroupArgs = new ConfigGroupArgs - { - ResourcePrefix = args.ResourcePrefix, - Objs = objs, - Transformations = args.Transformations - }; - var opts = ComponentResourceOptions.Merge(options, new ComponentResourceOptions { Parent = this }); - var resources = Parser.Parse(configGroupArgs, opts); + var childOpts = GetChildOptions(this, null, options); + var invokeOpts = GetInvokeOptions(childOpts); + var objs = Invokes.KustomizeDirectory(new KustomizeDirectoryArgs { Directory = args.Directory }, invokeOpts); + var resources = Parser.ParseYamlObjects(new ParseArgs + { + Objs = objs, + Transformations = args.Transformations, + ResourcePrefix = args.ResourcePrefix + }, childOpts); RegisterResources(resources); } private static string MakeName(DirectoryArgs? args, string name) => args?.ResourcePrefix != null ? $"{args.ResourcePrefix}-{name}" : name; - - private static string GetName(DirectoryArgs config, string releaseName) - { - var prefix = config.ResourcePrefix; - return string.IsNullOrEmpty(prefix) ? releaseName : $"{prefix}-{releaseName}"; - } - } /// diff --git a/sdk/dotnet/Yaml/ConfigFile.cs b/sdk/dotnet/Yaml/ConfigFile.cs index eec1e671e4..a984608bc0 100644 --- a/sdk/dotnet/Yaml/ConfigFile.cs +++ b/sdk/dotnet/Yaml/ConfigFile.cs @@ -125,39 +125,25 @@ public ConfigFile(string name, ConfigFileArgs? args = null, ComponentResourceOpt : base("kubernetes:yaml:ConfigFile", MakeName(args, name), options) { name = MakeName(args, name); - options ??= new ComponentResourceOptions(); - options.Parent ??= this; - - var transformations = args?.Transformations ?? new List(); - if (args?.SkipAwait == true) - { - transformations.Add(Parser.SkipAwait); - } + var fileOutput = args?.File.ToOutput() ?? Output.Create(name); + var childOpts = GetChildOptions(this, null, options); var resources = fileOutput.Apply(fileId => { try { - if (Parser.IsUrl(fileId)) - { - using var wc = new System.Net.WebClient(); - return wc.DownloadString(fileId); - } - - return File.ReadAllText(fileId); + return Parser.Parse(new ConfigGroupArgs{ + Files = new string[]{ fileId }, + Transformations = args?.Transformations ?? new List(), + ResourcePrefix = args?.ResourcePrefix, + SkipAwait = args?.SkipAwait + }, false, childOpts); } catch (Exception e) { throw new ResourceException($"Error fetching YAML file '{fileId}': {e.Message}", this); } - }).Apply(text => - Parser.ParseYamlDocument(new ParseArgs - { - Objs = Invokes.YamlDecode(new YamlDecodeArgs { Text = text }, new InvokeOptions { Provider = options?.Provider }), - Transformations = transformations, - ResourcePrefix = args?.ResourcePrefix - }, options)); - + }); RegisterResources(resources); } diff --git a/sdk/dotnet/Yaml/ConfigGroup.cs b/sdk/dotnet/Yaml/ConfigGroup.cs index 8d55c260fd..ac7409f809 100644 --- a/sdk/dotnet/Yaml/ConfigGroup.cs +++ b/sdk/dotnet/Yaml/ConfigGroup.cs @@ -205,9 +205,8 @@ public sealed class ConfigGroup : CollectionComponentResource public ConfigGroup(string name, ConfigGroupArgs config, ComponentResourceOptions? options = null) : base("kubernetes:yaml:ConfigGroup", name, options) { - options ??= new ComponentResourceOptions(); - options.Parent ??= this; - RegisterResources(Parser.Parse(config, options)); + var childOpts = GetChildOptions(this, null, options); + RegisterResources(Parser.Parse(config, true, childOpts)); } } diff --git a/sdk/dotnet/Yaml/Yaml.cs b/sdk/dotnet/Yaml/Yaml.cs index 20178c613b..faeeb52f61 100755 --- a/sdk/dotnet/Yaml/Yaml.cs +++ b/sdk/dotnet/Yaml/Yaml.cs @@ -821,61 +821,95 @@ public Output GetCustomResource(string groupVersionKind, string var id = namespaceName != null ? $"{namespaceName}/{name}" : name; return Resources.Apply(r => (CustomResource)r[$"{groupVersionKind}::{id}"]); } + + protected static CustomResourceOptions GetChildOptions(Pu.Resource parent, InputList? extraDependsOn, ComponentResourceOptions? options) + { + // Create resource options based on component resource options. + var dependsOn = new InputList(); + if (options?.DependsOn is not null) + dependsOn.AddRange(options.DependsOn); + if (extraDependsOn is not null) + dependsOn.AddRange(extraDependsOn); + return new CustomResourceOptions + { + Parent = parent, + DependsOn = dependsOn, + }; + } + + protected internal static InvokeOptions GetInvokeOptions(CustomResourceOptions? options) + { + return new InvokeOptions { + Parent = options?.Parent, + Provider = options?.Provider, + Version = options?.Version, + PluginDownloadURL = options?.PluginDownloadURL, + }.WithDefaults(); + } } internal static class Parser { - public static Output> Parse(ConfigGroupArgs config, ComponentResourceOptions? options) + public static Output> Parse(ConfigGroupArgs config, bool glob, CustomResourceOptions? opts) { var resources = Output.Create(ImmutableDictionary.Create()); var transformations = config.Transformations; - if (config?.SkipAwait == true) + if (config.SkipAwait == true) { transformations.Add(SkipAwait); } + var yamls = new InputList(); + if (config.Yaml != null) + yamls.AddRange(config.Yaml); + if (config.Files != null) { - var files = new List(); foreach (var file in config.Files) { + // Read the raw YAML file(s) specified in the input file parameter. It might be a URL or a file path. if (IsUrl(file)) - files.Add(file); - else - files.AddRange(Glob.Files(Directory.GetCurrentDirectory(), file)); - } - - foreach (var file in files) - { - var cf = new ConfigFile( - file, - new ConfigFileArgs - { - File = file, - Transformations = transformations, - ResourcePrefix = config.ResourcePrefix - }, - options); - resources = Output.Tuple(resources, cf.Resources).Apply(vs => vs.Item1.AddRange(vs.Item2)); + { + // If the string looks like a URL, in that it begins with a scheme, fetch it over the network. + using var wc = new System.Net.WebClient(); + var yaml = wc.DownloadString(file); + yamls.Add(yaml); + } + else + { + // Otherwise, assume this is a path to a file on disk. If globbing is enabled, we might have + // multiple files -- otherwise just read a singular file. + var files = new List(); + if (glob) + files.AddRange(Glob.Files(Directory.GetCurrentDirectory(), file)); + else + files.Add(file); + foreach (var fileId in files) + { + var yaml = File.ReadAllText(fileId); + yamls.Add(yaml); + } + } } } - if (config.Yaml != null) + if (yamls is not null) { - var yamlResources = config.Yaml.ToOutput().Apply(texts => + var invokeOpts = CollectionComponentResource.GetInvokeOptions(opts); + var yamlResources = yamls.ToOutput().Apply(texts => { - var yamls = texts + var r = texts .Select(text => - ParseYamlDocument(new ParseArgs + ParseYamlObjects(new ParseArgs { - Objs = Invokes.YamlDecode(new YamlDecodeArgs { Text = text }, new InvokeOptions { Provider = options?.Provider }), + Objs = Invokes.YamlDecode(new YamlDecodeArgs { Text = text }, invokeOpts), Transformations = transformations, - ResourcePrefix = config.ResourcePrefix - }, options)) + ResourcePrefix = config?.ResourcePrefix + }, opts)) .Select(output => (Input>)output) .ToImmutableArray(); - return Output.All(yamls); + return Output.All(r); }); resources = Output.Tuple(resources, yamlResources).Apply(vs => @@ -890,12 +924,12 @@ public static Output> Parse(Conf if (config.Objs != null) { - var docResources = ParseYamlDocument(new ParseArgs + var docResources = ParseYamlObjects(new ParseArgs { Objs = config.Objs, Transformations = transformations, ResourcePrefix = config.ResourcePrefix - }, options); + }, opts); resources = Output.Tuple(resources, docResources).Apply(vs => vs.Item1.AddRange(vs.Item2)); } @@ -927,14 +961,14 @@ internal static ImmutableDictionary SkipAwait(ImmutableDictionar internal static bool IsUrl(string s) => s.StartsWith("http://", StringComparison.Ordinal) || s.StartsWith("https://", StringComparison.Ordinal); - internal static Output> ParseYamlDocument(ParseArgs config, - ComponentResourceOptions? options = null) + internal static Output> ParseYamlObjects(ParseArgs config, + CustomResourceOptions? opts) { return config.Objs.ToOutput().Apply(objs => { var inputs = objs - .SelectMany(obj => ParseYamlObject(obj, config.Transformations, config.ResourcePrefix, options)) + .SelectMany(obj => ParseYamlObject(obj, config.Transformations, config.ResourcePrefix, opts)) .Select(output => (Input<(string, KubernetesResource)>) output) .ToImmutableArray(); @@ -946,21 +980,13 @@ internal static Output> ParseYam } private static Output<(string, KubernetesResource)>[] ParseYamlObject(ImmutableDictionary obj, - List? transformations, string? resourcePrefix, ComponentResourceOptions? options = null) + List? transformations, string? resourcePrefix, CustomResourceOptions? opts) { if (obj == null || obj.Count == 0) return new Output<(string, KubernetesResource)>[0]; - // Create custom resource options based on component resource options. - var opts = new CustomResourceOptions - { - Parent = options?.Parent, - DependsOn = options?.DependsOn ?? new InputList(), - IgnoreChanges = options?.IgnoreChanges ?? new List(), - Version = options?.Version, - Provider = options?.Provider, - CustomTimeouts = options?.CustomTimeouts - }; + // Create a copy of opts to pass into potentially mutating transforms that will be applied to this resource. + opts = CustomResourceOptions.Merge(null, opts); // Allow users to change API objects before any validation. if (transformations != null) @@ -1119,7 +1145,7 @@ internal static Output> ParseYam if (obj["items"] is IEnumerable> items) { foreach (var item in items) - objs.AddRange(Parser.ParseYamlObject(item, transformations, resourcePrefix)); + objs.AddRange(Parser.ParseYamlObject(item, transformations, resourcePrefix, opts)); } return objs.ToArray(); }