Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ GO_CMD_FLAGS=-tags codegen
.PHONY: all build-ack-generate test \
build-controller build-controller-image \
local-build-controller-image lint-shell \
check-crd-compatibility
check-crd-compatibility build-e2e-tests

all: test

Expand Down Expand Up @@ -51,6 +51,10 @@ CRD_PATHS ?= config/crd/bases,helm/crds
check-crd-compatibility: build-ack-generate ## Check CRDs for breaking changes against BASE_REF
@bin/ack-generate crd-compat-check --base-ref=$(BASE_REF) --crd-paths=$(CRD_PATHS)

build-e2e-tests: build-ack-generate ## Generate e2e test scaffolds for SERVICE
@echo "==== generating e2e tests for $(AWS_SERVICE)-controller ===="
@./scripts/build-e2e-tests.sh $(AWS_SERVICE)

test: ## Run code tests
go test ${GO_CMD_FLAGS} ./...

Expand Down
121 changes: 121 additions & 0 deletions cmd/ack-generate/command/e2e_tests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package command

import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"time"

"github.com/spf13/cobra"

ackgenconfig "github.com/aws-controllers-k8s/code-generator/pkg/config"
ackgenerate "github.com/aws-controllers-k8s/code-generator/pkg/generate/ack"
ackmetadata "github.com/aws-controllers-k8s/code-generator/pkg/metadata"
"github.com/aws-controllers-k8s/code-generator/pkg/sdk"
"github.com/aws-controllers-k8s/code-generator/pkg/util"
)

var (
optTestConfigPath string
)

var e2eTestsCmd = &cobra.Command{
Use: "e2e-tests <service>",
Short: "Generates Go e2e test files for an AWS service controller",
Long: `Generates Go e2e test scaffolds that exercise the CRUD lifecycle
for each resource defined in the controller's testconfig.yaml file.

The generated tests use the shared test library from test-infra/pkg/e2e/
and follow the create → wait-synced → update → delete pattern.`,
RunE: generateE2ETests,
}

func init() {
e2eTestsCmd.PersistentFlags().StringVar(
&optTestConfigPath, "test-config", "",
"Path to testconfig.yaml file. Defaults to <output-path>/testconfig.yaml",
)
rootCmd.AddCommand(e2eTestsCmd)
}

func generateE2ETests(cmd *cobra.Command, args []string) error {
cmdStart := time.Now()
if len(args) != 1 {
return fmt.Errorf("please specify the service alias for the AWS service API to generate")
}
svcAlias := strings.ToLower(args[0])
if optOutputPath == "" {
optOutputPath = filepath.Join(optServicesDir, svcAlias)
}

cfg, err := setupGenerator(svcAlias)
if err != nil {
return err
}

// Load testconfig.yaml
testConfigPath := optTestConfigPath
if testConfigPath == "" {
testConfigPath = filepath.Join(optOutputPath, "testconfig.yaml")
}
testCfg, err := ackgenconfig.NewTestConfig(testConfigPath)
if err != nil {
return fmt.Errorf("loading test config: %w", err)
}
if testCfg == nil {
return fmt.Errorf("testconfig.yaml not found at %s — create it to define test values for resources", testConfigPath)
}

// Load the AWS SDK model
metadata, err := ackmetadata.NewServiceMetadata(optMetadataConfigPath)
if err != nil {
return err
}
m, err := loadModelWithLatestAPIVersion(svcAlias, metadata, cfg)
if err != nil {
return err
}

// Generate test templates
ts, err := ackgenerate.E2ETests(m, optTemplateDirs, testCfg)
if err != nil {
return err
}

if err = ts.Execute(); err != nil {
return err
}

for path, contents := range ts.Executed() {
if optDryRun {
fmt.Printf("============================= %s ======================================\n", path)
fmt.Println(strings.TrimSpace(contents.String()))
continue
}
outPath := filepath.Join(optOutputPath, path)
outDir := filepath.Dir(outPath)
if _, err := sdk.EnsureDir(outDir); err != nil {
return err
}
if err = ioutil.WriteFile(outPath, contents.Bytes(), 0666); err != nil {
return err
}
}

util.Tracef("generateE2ETests total: %s\n", time.Since(cmdStart))
return nil
}
1 change: 0 additions & 1 deletion pkg/api/api_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/docstring.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/docstring_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/endpoint_trait.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/eventstream.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/eventstream_tmpl.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/eventstream_tmpl_reader.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import "text/template"
Expand Down
1 change: 0 additions & 1 deletion pkg/api/eventstream_tmpl_readertests.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/eventstream_tmpl_tests.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/eventstream_tmpl_writer.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import "text/template"
Expand Down
1 change: 0 additions & 1 deletion pkg/api/eventstream_tmpl_writertests.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/example.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/example_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/examples_builder.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/examples_builder_customizations.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

type wafregionalExamplesBuilder struct {
Expand Down
1 change: 0 additions & 1 deletion pkg/api/exportable_name.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import "strings"
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/legacy_io_suffix.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ var legacyIOSuffixed = IoSuffix{
"DatabaseInput": struct{}{},
"PartitionInput": struct{}{},
"ConnectionInput": struct{}{},
"DQTransformOutput": struct{}{},
"DQTransformOutput": struct{}{},
},

"Glacier": {
Expand Down
1 change: 0 additions & 1 deletion pkg/api/legacy_jsonvalue.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

type legacyJSONValues struct {
Expand Down
1 change: 0 additions & 1 deletion pkg/api/logger.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
9 changes: 4 additions & 5 deletions pkg/api/operation.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand All @@ -12,10 +11,10 @@ import (

// An Operation defines a specific API Operation.
type Operation struct {
API *API `json:"-"`
ExportedName string
Name string
Documentation string `json:"-"`
API *API `json:"-"`
ExportedName string
Name string
Documentation string `json:"-"`
// HTTP HTTPInfo
Host string `json:"host"`
InputRef ShapeRef `json:"input"`
Expand Down
1 change: 0 additions & 1 deletion pkg/api/pagination.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/param_filler.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/passes.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,6 @@ func createAPIParamShape(a *API, opName string, ref *ShapeRef, shapeName string,
return
}


if s, ok := a.Shapes[shapeName]; ok {
panic(fmt.Sprintf(
"attempting to create duplicate API parameter shape, %v, %v, %v, %v\n",
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/passes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,4 +1061,4 @@ func TestRenamedUnionShapePreservesOriginalName(t *testing.T) {
if unionShape.OriginalShapeName != "MemoryStrategyInput" {
t.Fatalf("expected OriginalShapeName %q after rename, got %q", "MemoryStrategyInput", unionShape.OriginalShapeName)
}
}
}
1 change: 0 additions & 1 deletion pkg/api/s3manger_input.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/service_name.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

// ServiceName returns the SDK's naming of the service. Has
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/shape_validation.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
2 changes: 0 additions & 2 deletions pkg/api/shape_value_builder.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/smoke.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
1 change: 0 additions & 1 deletion pkg/api/waiters.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package api

import (
Expand Down
67 changes: 67 additions & 0 deletions pkg/config/field_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package config

// FieldGroupOperationConfig defines an API operation that manages a subset of
// a resource's fields. Used in both update_operations and read_operations to
// specify per-field-group API calls.
//
// When the generator encounters these configs, it auto-detects which fields
// belong to each operation by inspecting the operation's Input/Output shape
// and cross-referencing with the resource's identifier fields (those shared
// with ReadOne/Delete operations). Explicitly listing fields in the Fields
// slice overrides this auto-detection.
//
// Example generator.yaml:
//
// resources:
// Repository:
// update_operations:
// - operation_id: PutImageScanningConfiguration
// requeue_on_success: true
// - operation_id: PutImageTagMutability
// read_operations:
// - operation_id: GetLifecyclePolicy
// - operation_id: GetRepositoryPolicy
type FieldGroupOperationConfig struct {
// OperationID is the SDK operation's exported name
// (e.g., "PutImageScanningConfiguration").
OperationID string `json:"operation_id"`
// Fields optionally overrides auto-detection of payload fields. When
// empty, payload fields are auto-detected from the operation's Input
// shape by excluding identifier fields. When set, only the listed CRD
// field names are treated as payload fields for this group.
Fields []string `json:"fields,omitempty"`
// RequeueOnSuccess, when true, causes the reconciler to requeue after
// this operation succeeds. This is useful for operations whose response
// does not contain the updated field values, requiring a subsequent
// ReadOne to refresh state.
RequeueOnSuccess bool `json:"requeue_on_success,omitempty"`
// Exceptions configures per-operation error handling. Currently
// supports a 404 error code mapping, identical in meaning to the
// resource-level exceptions.errors.404.code: when the operation
// returns this error code, treat it as "no data" rather than a
// failure (set the field to nil and continue).
Exceptions *FieldGroupExceptionsConfig `json:"exceptions,omitempty"`
}

// FieldGroupExceptionsConfig holds per-operation exception mappings.
type FieldGroupExceptionsConfig struct {
Errors map[int]FieldGroupErrorConfig `json:"errors,omitempty"`
}

// FieldGroupErrorConfig maps an HTTP status to an AWS error code.
type FieldGroupErrorConfig struct {
Code string `json:"code"`
}
Loading