func TestAZModeValidate(t *testing.T) {
	template := &parse.Template{}
	prop := schema.Schema{}
	ctx := schema.NewInitialContext(template, schema.NewResourceDefinitions(nil), schema.ValidationOptions{})

	singleAZCtx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("", map[string]interface{}{
				"PreferredAvailabilityZones": []interface{}{"one"},
			}),
			schema.Resource{},
		}),
		prop,
	)

	multiAZCtx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("", map[string]interface{}{
				"PreferredAvailabilityZones": []interface{}{"one", "two"},
			}),
			schema.Resource{},
		}),
		prop)

	if _, errs := azModeValidate("cross-az", singleAZCtx); errs == nil {
		t.Error("Should fail if cross-az with single availability zone", errs)
	}

	if _, errs := azModeValidate("cross-az", multiAZCtx); errs != nil {
		t.Error("Should pass if cross-az with multiple availability zones", errs)
	}
}
Exemple #2
0
func TestResourcePropertyConflictValidation(t *testing.T) {
	template := &parse.Template{}
	res := Resource{
		Properties: Properties{
			"Option1": Schema{
				Type:      ValueString,
				Conflicts: constraints.PropertyExists("Option2"),
			},

			"Option2": Schema{
				Type:      ValueString,
				Conflicts: constraints.PropertyExists("Option1"),
			},
		},
	}
	ctx := NewInitialContext(template, NewResourceDefinitions(map[string]Resource{
		"TestResource": res,
	}), ValidationOptions{})

	nothingSet := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{}),
		res,
	}
	option1Set := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option1": "value",
		}),
		res,
	}
	option2Set := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option2": "value",
		}),
		res,
	}
	bothSet := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option1": "value",
			"Option2": "value",
		}),
		res,
	}

	if _, errs := res.Validate(NewResourceContext(ctx, nothingSet)); errs != nil {
		t.Error("Resource should pass if both neither Option1 or Option2 are set", errs)
	}

	if _, errs := res.Validate(NewResourceContext(ctx, option1Set)); errs != nil {
		t.Error("Resource should pass if only Option1 set", errs)
	}

	if _, errs := res.Validate(NewResourceContext(ctx, option2Set)); errs != nil {
		t.Error("Resource should pass if only Option2 set", errs)
	}

	if _, errs := res.Validate(NewResourceContext(ctx, bothSet)); errs == nil {
		t.Error("Resource should fail if both Option1 or Option2 are set")
	}
}
func TestGeoLocationSubdivisionCodeValidation(t *testing.T) {
	template := &parse.Template{}
	res := schema.Resource{
		Properties: schema.Properties{
			"GeoLocation": schema.Schema{
				Type: geoLocation,
			},
		},
	}
	ctx := schema.NewInitialContext(template, schema.NewResourceDefinitions(map[string]schema.Resource{
		"TestResource": res,
	}), schema.ValidationOptions{})

	badCountryCtx := schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"GeoLocation": map[string]interface{}{
				"SubdivisionCode": "AK",
				"CountryCode":     "AU",
			},
		}),
		res,
	})

	badSubdivisionCtx := schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"GeoLocation": map[string]interface{}{
				"SubdivisionCode": "NSW",
				"CountryCode":     "US",
			},
		}),
		res,
	})

	goodCombinationCtx := schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"GeoLocation": map[string]interface{}{
				"SubdivisionCode": "AK",
				"CountryCode":     "US",
			},
		}),
		res,
	})

	if _, errs := res.Validate(goodCombinationCtx); errs != nil {
		t.Error("Period should pass on a valid state with US as the country", errs)
	}

	if _, errs := res.Validate(badSubdivisionCtx); errs == nil {
		t.Error("Period should fail on an invalid subdivision with US as the country")
	}

	if _, errs := res.Validate(badCountryCtx); errs == nil {
		t.Error("Period should fail when subdivision set without US as the country")
	}
}
Exemple #4
0
func TestPeriodValidation(t *testing.T) {
	template := &parse.Template{}
	self := ResourceWithDefinition{
		parse.NewTemplateResource("", make(map[string]interface{})),
		Resource{},
	}
	ctx := NewContextShorthand(template, NewResourceDefinitions(nil), self, Schema{}, ValidationOptions{})

	if _, errs := Period.Validate("", ctx); errs == nil {
		t.Error("Period should fail on empty string")
	}

	if _, errs := Period.Validate("abc", ctx); errs == nil {
		t.Error("Period should fail on anything which isn't a period")
	}

	for _, ex := range []string{"0", "10", "119", "260"} {
		if _, errs := Period.Validate(ex, ctx); errs == nil {
			t.Errorf("Period should fail on number which isn't a multiple of 60 (ex: %s)", ex)
		}
	}

	for _, ex := range []string{"60", "120", "240"} {
		if _, errs := Period.Validate(ex, ctx); errs != nil {
			t.Errorf("Cidr should pass with numbers which are multiples of 60 (ex: %s)", ex)
		}
	}
}
Exemple #5
0
func TestUnexpectedProperties(t *testing.T) {
	res := Resource{
		Properties: Properties{
			"Expected": Schema{
				Type: ValueString,
			},
		},
	}

	template := &parse.Template{}
	ctx := NewInitialContext(template, NewResourceDefinitions(map[string]Resource{
		"TestResource": res,
	}), ValidationOptions{})

	unexpected := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Expected":      "value",
			"SomethingElse": "value",
		}),
		res,
	}

	if _, errs := res.Validate(NewResourceContext(ctx, unexpected)); errs == nil {
		t.Error("Unexpected property should fail validation")
	}
}
Exemple #6
0
func TestCidrValidation(t *testing.T) {
	template := &parse.Template{}
	self := ResourceWithDefinition{
		parse.NewTemplateResource("", make(map[string]interface{})),
		Resource{},
	}
	ctx := NewContextShorthand(template, NewResourceDefinitions(nil), self, Schema{}, ValidationOptions{})

	if _, errs := CIDR.Validate("", ctx); errs == nil {
		t.Error("Cidr should fail on empty str, niling")
	}

	if _, errs := CIDR.Validate("abc", ctx); errs == nil {
		t.Error("Cidr should fail on anything which isn't a cidr")
	}

	if _, errs := CIDR.Validate("0.0.0.0/100", ctx); errs == nil {
		t.Error("Cidr should fail on an invalid mask")
	}

	if _, errs := CIDR.Validate("10.200.300.10/24", ctx); errs == nil {
		t.Error("Cidr should fail on an invalid IP")
	}

	if _, errs := CIDR.Validate("10.200.30.10/24", ctx); errs != nil {
		t.Error("Cidr should pass with a valid cidr")
	}
}
func TestNumCacheNodesValidate(t *testing.T) {
	template := &parse.Template{}
	prop := schema.Schema{}
	ctx := schema.NewInitialContext(template, schema.NewResourceDefinitions(nil), schema.ValidationOptions{})

	redisCtx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("", map[string]interface{}{
				"Engine": "redis",
			}),
			schema.Resource{},
		}),
		prop)

	memcachedCtx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("", map[string]interface{}{
				"Engine": "memcached",
			}),
			schema.Resource{},
		}),
		prop)

	if _, errs := numCacheNodesValidate(float64(1), redisCtx); errs != nil {
		t.Error("Should pass with 1 redis node", errs)
	}

	if _, errs := numCacheNodesValidate(float64(2), redisCtx); errs == nil {
		t.Error("Should fail with more than 1 redis node", errs)
	}

	if _, errs := numCacheNodesValidate(float64(1), memcachedCtx); errs != nil {
		t.Error("Should pass with 1 memcached node", errs)
	}

	if _, errs := numCacheNodesValidate(float64(20), memcachedCtx); errs != nil {
		t.Error("Should pass with 20 memcached nodes", errs)
	}

	if _, errs := numCacheNodesValidate(float64(21), memcachedCtx); errs == nil {
		t.Error("Should fail with 21 memcached nodes", errs)
	}
}
func TestAllowedMethodsFixedArrays(t *testing.T) {
	res := Distribution
	template := &parse.Template{}

	ctx := schema.NewInitialContext(template, schema.NewResourceDefinitions(map[string]schema.Resource{
		"TestResource": res,
	}), schema.ValidationOptions{})

	testCFDistribution := func(allowedMethods []interface{}) schema.ResourceContext {
		return schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("TestResource", map[string]interface{}{
				"DistributionConfig": map[string]interface{}{
					"Enabled": true,
					"DefaultCacheBehavior": map[string]interface{}{
						"AllowedMethods":       allowedMethods,
						"TargetOriginId":       "test",
						"ViewerProtocolPolicy": "test",
						"ForwardedValues": map[string]interface{}{
							"QueryString": true,
						},
					},
					"Origins": []interface{}{
						map[string]interface{}{
							"Id":         "test",
							"DomainName": "test",
							"CustomOriginConfig": map[string]interface{}{
								"OriginProtocolPolicy": "test",
							},
						},
					},
				},
			}),
			res,
		})
	}

	if _, errs := res.Validate(testCFDistribution([]interface{}{"HEAD", "GET"})); errs != nil {
		t.Error("Should pass with expected array", errs)
	}

	if _, errs := res.Validate(testCFDistribution([]interface{}{"GET", "HEAD"})); errs != nil {
		t.Error("Should pass with expected array in different order", errs)
	}

	if _, errs := res.Validate(testCFDistribution([]interface{}{"DELETE", "GET", "HEAD"})); errs == nil {
		t.Error("Should fail with random subset")
	}

	if _, errs := res.Validate(testCFDistribution([]interface{}{"GET", "HEAD", "somethingElse"})); errs == nil {
		t.Error("Should fail with unexpected item")
	}
}
Exemple #9
0
// TODO: This is all a bit hairy. We shouldn't need to be creating the
// 			 TemplateNestedResource here, ideally `self` should already refer to
//			 one and value should already be a map[string]inteface{}
func (res NestedResource) Validate(value interface{}, ctx PropertyContext) (reporting.ValidateResult, reporting.Reports) {
	if values, ok := value.(map[string]interface{}); ok {
		property := ctx.Property()
		tnr := parse.NewTemplateResource(property.Type.Describe(), values)

		nestedResourceContext := NewResourceContext(ctx, ResourceWithDefinition{tnr, property.Type})
		failures := res.Properties.Validate(nestedResourceContext)

		return reporting.ValidateOK, reporting.Safe(failures)
	}

	return reporting.ValidateOK, reporting.Reports{reporting.NewFailure(ctx, "Invalid type %T for nested resource %s", value, res.Description)}
}
Exemple #10
0
func TestSchemaMapToArrayCoercion(t *testing.T) {
	res := parse.NewTemplateResource("TestType", map[string]interface{}{
		"NestedArrayProp": map[string]interface{}{
			"StringProp": "blah",
		},
	})

	prop := Schema{
		Type: Multiple(NestedResource{
			Description: "TestType",
			Properties: Properties{
				"StringProp": Schema{
					Type:     ValueString,
					Required: constraints.Always,
				},
			},
		}),
	}

	def := Resource{
		Properties: Properties{
			"NestedArrayProp": prop,
		},
	}

	defaultOptionsBase := NewInitialContext(&parse.Template{}, NewResourceDefinitions(nil), ValidationOptions{})
	defaultOptionsCtx := NewResourceContext(defaultOptionsBase, ResourceWithDefinition{res, def})

	mapCoercionOptionsBase := NewInitialContext(&parse.Template{}, NewResourceDefinitions(nil), ValidationOptions{
		OptionExperimentDisableObjectArrayCoercion: true,
	})
	mapCoercionOptionsCtx := NewResourceContext(mapCoercionOptionsBase, ResourceWithDefinition{res, def})

	value := map[string]interface{}{
		"StringProp": "blah",
	}
	if _, errs := prop.Validate(value, mapCoercionOptionsCtx); errs == nil {
		t.Error("Shouldn't be able to coerce a map into an array property", errs)
	}

	if _, errs := prop.Validate(value, defaultOptionsCtx); errs == nil {
		t.Error("Shouldn't be able to coerce a map into an array property without a warning", errs, len(errs))
	} else if errs[0].Level != reporting.Warning {
		t.Error("Shouldn't be able to coerce a map into an array property without a warning", errs, len(errs))
	}
}
Exemple #11
0
func TestEnumValidation(t *testing.T) {
	template := &parse.Template{}
	self := ResourceWithDefinition{
		parse.NewTemplateResource("", make(map[string]interface{})),
		Resource{},
	}
	ctx := NewContextShorthand(template, NewResourceDefinitions(nil), self, Schema{}, ValidationOptions{})

	enum := EnumValue{
		Options: []string{"a", "b", "c"},
	}

	if _, errs := enum.Validate("", ctx); errs == nil {
		t.Error("Enum should fail on empty string")
	}

	if _, errs := enum.Validate("d", ctx); errs == nil {
		t.Error("Enum should fail on anything which isn't a valid option")
	}

	if _, errs := enum.Validate("b", ctx); errs != nil {
		t.Error("Enum should pass on a valid option")
	}
}
Exemple #12
0
func TestDeprecatedProperties(t *testing.T) {
	res := Resource{
		Properties: Properties{
			"Deprecated": Schema{
				Type:       ValueString,
				Deprecated: deprecations.Deprecated("blah blah."),
			},

			"DeprecatedBy": Schema{
				Type:       ValueString,
				Deprecated: deprecations.ReplacedBy("SomethingElse", "blah blah."),
			},
		},
	}

	template := &parse.Template{}
	ctx := NewInitialContext(template, NewResourceDefinitions(map[string]Resource{
		"TestResource": res,
	}), ValidationOptions{})

	unexpected := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Deprecated":   "value",
			"DeprecatedBy": "value",
		}),
		res,
	}

	if _, errs := res.Validate(NewResourceContext(ctx, unexpected)); !hasWarning(errs, "Deprecated: blah blah.") {
		t.Errorf("Deprecated property use should warn (errs: %s)", errs)
	}

	if _, errs := res.Validate(NewResourceContext(ctx, unexpected)); !hasWarning(errs, "Replaced by SomethingElse: blah blah.") {
		t.Errorf("Deprecated property use should warn (errs: %s)", errs)
	}
}
func TestAutomaticFailoverEnabled(t *testing.T) {
	template := &parse.Template{}
	res := ReplicationGroup
	ctx := schema.NewInitialContext(template, schema.NewResourceDefinitions(map[string]schema.Resource{
		"TestResource": res,
	}), schema.ValidationOptions{})

	badVersionCtx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("TestResource", map[string]interface{}{
				"EngineVersion": "2.7",
				"CacheNodeType": "cache.m3.medium",
			}),
			res,
		}),
		schema.Schema{})

	badNodeTypeT1Ctx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("TestResource", map[string]interface{}{
				"EngineVersion": "2.8",
				"CacheNodeType": "cache.t1.micro",
			}),
			res,
		}),
		schema.Schema{})

	badNodeTypeT2Ctx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("TestResource", map[string]interface{}{
				"EngineVersion": "2.8",
				"CacheNodeType": "cache.t2.micro",
			}),
			res,
		}),
		schema.Schema{})

	goodCtx := schema.NewPropertyContext(
		schema.NewResourceContext(ctx, schema.ResourceWithDefinition{
			parse.NewTemplateResource("TestResource", map[string]interface{}{
				"EngineVersion": "2.8",
				"CacheNodeType": "cache.m3.medium",
			}),
			res,
		}),
		schema.Schema{})

	if _, errs := automaticFailoverEnabledValidation(true, badVersionCtx); errs == nil {
		t.Error("Should fail if has engine less than 2.8")
	}

	if _, errs := automaticFailoverEnabledValidation(true, badNodeTypeT1Ctx); errs == nil {
		t.Error("Should fail if has node type of t1 or t2")
	}

	if _, errs := automaticFailoverEnabledValidation(true, badNodeTypeT2Ctx); errs == nil {
		t.Error("Should fail if has node type of t1 or t2")
	}

	if _, errs := automaticFailoverEnabledValidation(true, goodCtx); errs != nil {
		t.Error("Should pass if engine is 2.8 or above and node type isn't t1 or t2")
	}
}
Exemple #14
0
func TestSchemaRequiredValidation(t *testing.T) {
	template := &parse.Template{}
	res := Resource{
		Properties: Properties{
			"Option1": Schema{
				Type:     ValueString,
				Required: constraints.Always,
			},

			"Option2": Schema{
				Type:     ValueString,
				Required: constraints.Never,
			},

			"Option3": Schema{
				Type: ValueString,
			},
		},
	}
	ctx := NewInitialContext(template, NewResourceDefinitions(map[string]Resource{
		"TestResource": res,
	}), ValidationOptions{})

	nothingSet := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{}),
		res,
	}
	option1Set := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option1": "value",
		}),
		res,
	}
	option2Set := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option2": "value",
		}),
		res,
	}
	option3Set := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option3": "value",
		}),
		res,
	}
	allSet := ResourceWithDefinition{
		parse.NewTemplateResource("TestResource", map[string]interface{}{
			"Option1": "value",
			"Option2": "value",
			"Option3": "value",
		}),
		res,
	}

	if _, errs := res.Validate(NewResourceContext(ctx, nothingSet)); errs == nil {
		t.Error("Resource should fail if Option1 isn't set", errs)
	}

	if _, errs := res.Validate(NewResourceContext(ctx, option1Set)); errs != nil {
		t.Error("Resource should pass if only Option1 set", errs)
	}

	if _, errs := res.Validate(NewResourceContext(ctx, option2Set)); errs == nil {
		t.Error("Resource should fail if only Option2 set")
	}

	if _, errs := res.Validate(NewResourceContext(ctx, option3Set)); errs == nil {
		t.Error("Resource should fail if only Option3 set")
	}

	if _, errs := res.Validate(NewResourceContext(ctx, allSet)); errs != nil {
		t.Error("Resource should pass if Option1 is set with others", errs)
	}
}
func TestNestedResourceConstraints(t *testing.T) {
	res := Resource{
		Properties: Properties{
			"Nested": Schema{
				Type: NestedResource{
					Properties: Properties{
						"One": Schema{
							Type: ValueString,
						},

						"Two": Schema{
							Type:     ValueString,
							Required: constraints.PropertyExists("One"),
						},
					},
				},
			},
		},
	}

	template := &parse.Template{}
	data := func(properties map[string]interface{}) ResourceContext {
		return NewContextShorthand(
			template,
			NewResourceDefinitions(map[string]Resource{
				"TestResource": res,
			}),
			ResourceWithDefinition{
				parse.NewTemplateResource("TestResource", properties),
				res,
			},
			Schema{},
			ValidationOptions{},
		)
	}

	twoMissing := map[string]interface{}{
		"Nested": map[string]interface{}{
			"One": "abc",
		},
	}
	if _, errs := res.Validate(data(twoMissing)); errs == nil {
		t.Error("Should fail with missing Two parameter")
	}

	oneInWrongPace := map[string]interface{}{
		"One":    "abc",
		"Nested": map[string]interface{}{},
	}
	if _, errs := res.Validate(data(oneInWrongPace)); errs == nil {
		t.Error("Should fail with missing Two parameter")
	}

	allFine := map[string]interface{}{
		"Nested": map[string]interface{}{
			"One": "abc",
			"Two": "abc",
		},
	}
	if _, errs := res.Validate(data(allFine)); errs != nil {
		t.Error("Should pass with One and Two", errs)
	}
}