From 455f522412617201e60d871b2ef9f1287973337e Mon Sep 17 00:00:00 2001 From: Amine Hilaly Date: Thu, 17 Jun 2021 00:32:11 +0200 Subject: [PATCH 1/2] Move binary version variables to a seperate package --- Makefile | 7 +++--- cmd/ack-generate/command/root.go | 9 +------- cmd/ack-generate/command/version.go | 6 ++--- cmd/ack-generate/main.go | 11 +--------- pkg/version/version.go | 34 +++++++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 pkg/version/version.go diff --git a/Makefile b/Makefile index ded9a08c..d33983be 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,10 @@ AWS_SERVICE=$(shell echo $(SERVICE) | tr '[:upper:]' '[:lower:]') VERSION ?= "v0.0.0" GITCOMMIT=$(shell git rev-parse HEAD) BUILDDATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') -GO_LDFLAGS=-ldflags "-X main.version=$(VERSION) \ - -X main.buildHash=$(GITCOMMIT) \ - -X main.buildDate=$(BUILDDATE)" +IMPORT_PATH=github.com/aws-controllers-k8s/code-generator +GO_LDFLAGS=-ldflags "-X $(IMPORT_PATH)/pkg/version.Version=$(VERSION) \ + -X $(IMPORT_PATH)/pkg/version.BuildHash=$(GITCOMMIT) \ + -X $(IMPORT_PATH)/pkg/version.BuildDate=$(BUILDDATE)" # We need to use the codegen tag when building and testing because the # aws-sdk-go/private/model/api package is gated behind a build tag "codegen"... diff --git a/cmd/ack-generate/command/root.go b/cmd/ack-generate/command/root.go index 9bf17df4..e8cfbfa4 100644 --- a/cmd/ack-generate/command/root.go +++ b/cmd/ack-generate/command/root.go @@ -30,9 +30,6 @@ A tool to generate AWS service controller code` ) var ( - version string - buildHash string - buildDate string defaultCacheDir string optCacheDir string optRefreshCache bool @@ -125,11 +122,7 @@ func init() { // Execute adds all child commands to the root command and sets flags // appropriately. This is called by main.main(). It only needs to happen once // to the rootCmd. -func Execute(v string, bh string, bd string) { - version = v - buildHash = bh - buildDate = bd - +func Execute() { if err := rootCmd.Execute(); err != nil { os.Exit(1) } diff --git a/cmd/ack-generate/command/version.go b/cmd/ack-generate/command/version.go index 505a9732..28661b8e 100644 --- a/cmd/ack-generate/command/version.go +++ b/cmd/ack-generate/command/version.go @@ -15,9 +15,10 @@ package command import ( "fmt" - "runtime" "github.com/spf13/cobra" + + "github.com/aws-controllers-k8s/code-generator/pkg/version" ) const debugHeader = `Date: %s @@ -31,8 +32,7 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "Display the version of " + appName, Run: func(cmd *cobra.Command, args []string) { - goVersion := fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH) - fmt.Printf(debugHeader, buildDate, goVersion, version, buildHash) + fmt.Printf(debugHeader, version.BuildDate, version.GoVersion, version.Version, version.BuildHash) }, } diff --git a/cmd/ack-generate/main.go b/cmd/ack-generate/main.go index 9161126d..6daea4a0 100644 --- a/cmd/ack-generate/main.go +++ b/cmd/ack-generate/main.go @@ -17,15 +17,6 @@ import ( "github.com/aws-controllers-k8s/code-generator/cmd/ack-generate/command" ) -var ( - // version of application at compile time (-X 'main.version=$(VERSION)'). - version = "(Unknown Version)" - // buildHash GIT hash of application at compile time (-X 'main.buildHash=$(GITCOMMIT)'). - buildHash = "No Git-hash Provided." - // buildDate of application at compile time (-X 'main.buildDate=$(BUILDDATE)'). - buildDate = "No Build Date Provided." -) - func main() { - command.Execute(version, buildHash, buildDate) + command.Execute() } diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 00000000..508adfd2 --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,34 @@ +// 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 version + +import ( + "fmt" + "runtime" +) + +var ( + // BuildDate of application at compile time (-X 'main.buildDate=$(BUILDDATE)'). + BuildDate string = "No Build Date Provided." + // Version of application at compile time (-X 'main.version=$(VERSION)'). + Version string = "(Unknown Version)" + // BuildHash is the GIT hash of application at compile time (-X 'main.buildHash=$(GITCOMMIT)'). + BuildHash string = "No Git-hash Provided." + // GoVersion is the Go compiler version used to compile this binary + GoVersion string +) + +func init() { + GoVersion = fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH) +} From 1f90f1a2ed6dbb5b8657786c9027ae8e9dcedb71 Mon Sep 17 00:00:00 2001 From: Amine Hilaly Date: Thu, 17 Jun 2021 16:38:38 +0200 Subject: [PATCH 2/2] Add a cobra PostRunE function to save generation metadata within the API directory - Add an output files containing utilities and functions to create/save the generation metadata files --- cmd/ack-generate/command/apis.go | 20 +++- pkg/generate/ack/output.go | 195 +++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 pkg/generate/ack/output.go diff --git a/cmd/ack-generate/command/apis.go b/cmd/ack-generate/command/apis.go index 3072fb68..435a7955 100644 --- a/cmd/ack-generate/command/apis.go +++ b/cmd/ack-generate/command/apis.go @@ -42,9 +42,10 @@ var ( // apiCmd is the command that generates service API types var apisCmd = &cobra.Command{ - Use: "apis ", - Short: "Generate Kubernetes API type definitions for an AWS service API", - RunE: generateAPIs, + Use: "apis ", + Short: "Generate Kubernetes API type definitions for an AWS service API", + RunE: generateAPIs, + PostRunE: saveGeneratedMetadata, } func init() { @@ -54,6 +55,19 @@ func init() { rootCmd.AddCommand(apisCmd) } +// saveGeneratedMetadata saves the parameters used to generate APIs and checksum +// of the generated code. +func saveGeneratedMetadata(cmd *cobra.Command, args []string) error { + err := ackgenerate.CreateGenerationMetadata( + optGenVersion, + filepath.Join(optOutputPath, "apis"), + ackgenerate.UpdateReasonAPIGeneration, + optAWSSDKGoVersion, + optGeneratorConfigPath, + ) + return err +} + // generateAPIs generates the Go files for each resource in the AWS service // API. func generateAPIs(cmd *cobra.Command, args []string) error { diff --git a/pkg/generate/ack/output.go b/pkg/generate/ack/output.go new file mode 100644 index 00000000..077e48eb --- /dev/null +++ b/pkg/generate/ack/output.go @@ -0,0 +1,195 @@ +// 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 ack + +import ( + "crypto/sha1" + "encoding/hex" + "io" + "io/ioutil" + "os" + "path/filepath" + "time" + + "github.com/ghodss/yaml" + + "github.com/aws-controllers-k8s/code-generator/pkg/version" +) + +const ( + outputFileName = "ack-generate-metadata.yaml" +) + +// UpdateReason is the reason a package got modified. +type UpdateReason string + +const ( + // UpdateReasonAPIGeneration should be used when an API package + // is modified by the APIs generator (ack-generate apis). + UpdateReasonAPIGeneration UpdateReason = "API generation" + + // UpdateReasonConversionFunctionsGeneration Should be used when + // an API package is modified by conversion functions generator. + // TODO(hilalymh) ack-generate conversion-functions + UpdateReasonConversionFunctionsGeneration UpdateReason = "Conversion functions generation" +) + +// GenerationMetadata represents the parameters used to generate/update the +// API version directory. +// +// This type is public because soon it will be used by conversion generators +// to load APIs generation metadata. +// TODO(hilalymh) Add functions to load/edit metadata files. +type GenerationMetadata struct { + // The APIs version e.g v1alpha2 + APIVersion string `json:"api_version"` + // The checksum of all the combined files generated within the APIs directory + APIDirectoryChecksum string `json:"api_directory_checksum"` + // Last modification reason + LastModification lastModificationInfo `json:"last_modification"` + // AWS SDK Go version used generate the APIs + AWSSDKGoVersion string `json:"aws_sdk_go_version"` + // Informatiom about the ack-generate binary used to generate the APIs + ACKGenerateInfo ackGenerateInfo `json:"ack_generate_info"` + // Information about the generator config file used to generate the APIs + GeneratorConfigInfo generatorConfigInfo `json:"generator_config_info"` +} + +// ack-generate binary information +type ackGenerateInfo struct { + Version string `json:"version"` + GoVersion string `json:"go_version"` + BuildDate string `json:"build_date"` + BuildHash string `json:"build_hash"` +} + +// generator.yaml information +type generatorConfigInfo struct { + OriginalFileName string `json:"original_file_name"` + FileChecksum string `json:"file_checksum"` +} + +// last modification information +type lastModificationInfo struct { + // UTC Timestamp + Timestamp string `json:"timestamp"` + // Modification reason + Reason UpdateReason `json:"reason"` +} + +// CreateGenerationMetadata gathers information about the generated code and save +// a yaml version in the API version directory +func CreateGenerationMetadata( + apiVersion string, + apisPath string, + modificationReason UpdateReason, + awsSDKGo string, + generatorFileName string, +) error { + filesDirectory := filepath.Join(apisPath, apiVersion) + hash, err := hashDirectoryContent(filesDirectory) + if err != nil { + return err + } + + generatorFileHash, err := hashFile(generatorFileName) + if err != nil { + return err + } + + generationMetadata := &GenerationMetadata{ + APIVersion: apiVersion, + APIDirectoryChecksum: hash, + LastModification: lastModificationInfo{ + Timestamp: time.Now().UTC().String(), + Reason: modificationReason, + }, + AWSSDKGoVersion: awsSDKGo, + ACKGenerateInfo: ackGenerateInfo{ + Version: version.Version, + BuildDate: version.BuildDate, + BuildHash: version.BuildHash, + GoVersion: version.GoVersion, + }, + GeneratorConfigInfo: generatorConfigInfo{ + OriginalFileName: filepath.Base(generatorFileName), + FileChecksum: generatorFileHash, + }, + } + + data, err := yaml.Marshal(generationMetadata) + if err != nil { + return err + } + + outputFileName := filepath.Join(filesDirectory, outputFileName) + err = ioutil.WriteFile( + outputFileName, + data, + os.ModePerm, + ) + if err != nil { + return err + } + return nil +} + +// hashDirectoryContent returns the sha1 checksum of a given directory. It will walk +// the file tree of a directory and combine and the file contents before hashing it. +func hashDirectoryContent(directory string) (string, error) { + h := sha1.New() + err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + // ignore yaml files (output.yaml and generator.yaml) + fileExtension := filepath.Ext(info.Name()) + if fileExtension == ".yaml" { + return nil + } + + fileReader, err := os.Open(path) + if err != nil { + return err + } + _, err = io.Copy(h, fileReader) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + return "", err + } + + hash := hex.EncodeToString(h.Sum(nil)) + return hash, nil +} + +// hashFile returns the sha1 hash of a given file +func hashFile(filename string) (string, error) { + h := sha1.New() + fileReader, err := os.Open(filename) + if err != nil { + return "", err + } + _, err = io.Copy(h, fileReader) + if err != nil { + return "", err + } + hash := hex.EncodeToString(h.Sum(nil)) + return hash, nil +}