/// <summary> /// Initializes a new instance of the <see cref="Scalar"/> class. /// </summary> /// <param name="value">The value.</param> /// <param name="isQuoted">if set to <c>true</c> [is quoted].</param> public Scalar(object value, bool isQuoted) { this.IsQuoted = isQuoted; switch (value) { case null: return; // ReSharper disable once AssignNullToNotNullAttribute - typeof an existing type cannot be null case string s when s.StartsWith(typeof(Reference).Namespace): { // Re-hydrate a smuggled in "Reference" type. var split = s.Split(':'); var type = Assembly.GetCallingAssembly().GetType(split[0]); var reference = (Reference)Activator.CreateInstance(type, split[1]); this.Value = reference.ReferenceExpression; this.IsQuoted = false; return; } case bool _: this.Value = value.ToString().ToLowerInvariant(); this.IsQuoted = false; break; default: { this.Value = value.ToString(); if (string.IsNullOrWhiteSpace(this.Value)) { return; } this.IsJsonDocument = StateFileSerializer.TryGetJson( this.Value, false, "Unknown", "Unknown", out var document); if (this.IsJsonDocument) { this.JsonDocumentType = document.Type; } break; } } }
public void ShouldSerializeResource(string stateFile) { this.output.WriteLine($"Serialize {stateFile}"); var state = JsonConvert.DeserializeObject <StateFile>( (string)ResourceLoader.GetStringResource(ResourceLoader.GetResourceStream(stateFile, ThisAssembly))); using var sw = new StringWriter(); var events = new List <HclEvent>(); var emitter = new he(sw); var serializer = new StateFileSerializer(emitter); var action = new Action(() => serializer.Serialize(state)); action.Should().NotThrow <HclSerializerException>(); this.fixture.Validate(sw.ToString(), new TestLogger(this.output)).Should().BeTrue(); }
/// <summary> /// Visits a string value in the JSON object graph. /// Look to see if this matches the evaluation of any CloudFormation intrinsic /// and generate a <see cref="StateModification"/> where a match is found. /// </summary> /// <param name="jsonValue">A JValue of type string.</param> /// <param name="context">The visitor context.</param> protected override void VisitString(JValue jsonValue, TerraformAttributeSetterContext context) { if (context.IsComputedValue(GetParentPropertyPath(jsonValue))) { // Don't adjust computed attributes return; } var stringValue = jsonValue.Value <string>(); if (StateFileSerializer.TryGetJson( stringValue, false, context.Resource.Name, context.Resource.Type, out var document)) { try { context.EnterNestedJson((JProperty)jsonValue.Parent); // Build a new visitor to visit the nested JSON document document.Accept(new TerraformAttributeSetterVisitor(), context); } finally { context.ExitNestedJson(); } return; } var intrinsicInfo = context.IntrinsicInfos.FirstOrDefault(i => i.Evaluation.ToString() == stringValue); if (intrinsicInfo == null) { // No intrinsic evaluation matches this JValue return; } CreateModification(jsonValue, context, intrinsicInfo); }
/// <summary> /// Resolves the dependencies for the given terraform resource. /// </summary> /// <param name="terraformStateFileResource">The current terraform resource from state file.</param> public void ResolveResourceDependencies(StateFileResourceDeclaration terraformStateFileResource) { try { var referenceLocations = new List <IntrinsicInfo>(); // Get CF resource for the current state file resource entry this.currentCloudFormationResource = this.settings.Resources.First(r => r.LogicalResourceId == terraformStateFileResource.Name); // Find related resources that may be merged into this one var relatedResources = this.template.DependencyGraph.Edges.Where( e => e.Source.Name == this.currentCloudFormationResource.LogicalResourceId && e.Target.TemplateObject is IResource res && TerraformExporterConstants.MergedResources.Contains(res.Type)) .Select(e => (IResource)e.Target.TemplateObject); foreach (var cloudFormationResource in new[] { this.currentCloudFormationResource.TemplateResource } .Concat(relatedResources)) { // Visit the CF resource gathering all intrinsics that might imply reference to another resource or input var intrinsicVisitorContext = new IntrinsicVisitorContext( this.settings, this.terraformResources, this.module.Inputs, cloudFormationResource, this.warnings, this.module); var intrinsicVisitor = new IntrinsicVisitor(this.template); cloudFormationResource.Accept(intrinsicVisitor, intrinsicVisitorContext); referenceLocations.AddRange(intrinsicVisitorContext.ReferenceLocations); } // Visit the terraform resource finding value matches between resource attributes and intrinsic evaluations, recording what needs to be modified var dependencyContext = new TerraformAttributeSetterContext( referenceLocations, this.template, terraformStateFileResource, this.module.Inputs); terraformStateFileResource.ResourceInstance.Attributes.Accept( new TerraformAttributeSetterVisitor(), dependencyContext); // For each found modification, update attribute value with JSON encoded reference expression or string interpolation. foreach (var modification in dependencyContext.Modifications.Where(m => m.Reference != null)) { if (modification.ContainingProperty == null) { // Normal resource attribute ApplyResourceAttributeReference( terraformStateFileResource.ResourceInstance.Attributes.SelectToken( modification.ValueToReplace.Path), modification); } else { // Modification lies within nested JSON, e.g. a policy document. // First, deserialize the nested JSON - which will work because we already did it once. StateFileSerializer.TryGetJson( modification.ContainingProperty.Value.Value <string>(), false, terraformStateFileResource.Name, terraformStateFileResource.Type, out var document); ApplyResourceAttributeReference( document.SelectToken(modification.ValueToReplace.Path), modification); // Now put back the nested JSON complete with added reference var enc = document.ToString(Formatting.None); modification.ContainingProperty.Value = enc; } } } catch (HclSerializerException) { throw; } catch (Exception e) { throw new HclSerializerException( $"Internal error: {e.Message}", terraformStateFileResource.Name, terraformStateFileResource.Type, e); } }