From 7ff69b1e8f84bb8408d2b7aed29454ca6ba2938d Mon Sep 17 00:00:00 2001 From: Deepti Vaidyanathan Date: Mon, 6 Jan 2025 14:37:10 -0800 Subject: [PATCH 1/4] Adds error clarification codes --- pkg/errorutil/errorclarificationcodes.go | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 pkg/errorutil/errorclarificationcodes.go diff --git a/pkg/errorutil/errorclarificationcodes.go b/pkg/errorutil/errorclarificationcodes.go new file mode 100644 index 0000000..cad4274 --- /dev/null +++ b/pkg/errorutil/errorclarificationcodes.go @@ -0,0 +1,49 @@ +package errorutil + +import ( + "math" +) + +const ( + // System errors + fileDownload_badRequest int = -41 + fileDownload_unknownError int = -40 + + imds_internalMsiError int = -30 + + internal_badConfig int = -21 + internal_couldNotFindCertificate int = -20 + + storage_internalServerError int = -1 + systemError int = 0 // CRP interprets anything > 0 as user errors + + // User errors + commandExecution_failedUnknownError int = 1 + commandExecution_failureExitCode int = 2 + commandExecution_interruptedByVmShutdown int = 3 + + customerInput_commandToExecuteSpecifiedInTwoPlaces int = 20 + customerInput_ignoreRelativePathForFileDownloadsSpecifiedInTwoPlaces int = 21 + customerInput_fileUrisSpecifiedInTwoPlaces int = 22 + customerInput_commandToExecuteNotSpecified int = 23 + customerInput_fileUriContainsNull int = 24 + customerInput_invalidFileUris int = 25 + customerInput_storageCredsAndMIBothSpecified int = 26 + customerInput_clientIdObjectIdBothSpecified int = 27 + + fileDownload_unableToCreateDownloadDirectory int = 50 + fileDownload_sasExpired int = 51 + fileDownload_accessDenied int = 52 + fileDownload_doesNotExist int = 53 + fileDownload_networkingError int = 54 + fileDownload_genericError int = 55 + fileDownload_exceededTimeout int = 56 + + msi_notFound int = 70 + msi_doesNotHaveRightPermissions int = 71 + msi_GenericRetrievalError int = 72 + + // No Error - used as a placeholder value + // when representing an "empty" ErrorWithClarification + noError int = math.MaxInt +) From 3acd0b5d20cfe2969895f94b9d25786ab5462d58 Mon Sep 17 00:00:00 2001 From: Deepti Vaidyanathan Date: Tue, 7 Jan 2025 14:02:43 -0800 Subject: [PATCH 2/4] Initial integration of user error clarifications --- go.mod | 28 +++++++-- go.sum | 79 ++++++++++++++++++++++++ main/cmds.go | 44 +++++++------ main/exec.go | 8 ++- main/files.go | 37 ++++++----- main/handlersettings.go | 38 +++++++----- main/main.go | 10 +-- main/status.go | 24 +++++++ pkg/download/downloader.go | 31 ++++++++-- pkg/download/retry.go | 18 +++--- pkg/download/save.go | 14 +++-- pkg/errorutil/errorclarificationcodes.go | 18 +++--- 12 files changed, 261 insertions(+), 88 deletions(-) diff --git a/go.mod b/go.mod index 4564042..44f9d88 100644 --- a/go.mod +++ b/go.mod @@ -1,31 +1,47 @@ module github.com/Azure/custom-script-extension-linux -go 1.21 +go 1.23 + +toolchain go1.23.4 require ( github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 - github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954 - github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible + github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6 + github.com/Azure/azure-sdk-for-go v68.0.0+incompatible github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 - github.com/go-kit/kit v0.12.0 - github.com/google/uuid v1.3.0 + github.com/go-kit/kit v0.13.0 + github.com/google/uuid v1.6.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/text v0.9.0 + golang.org/x/text v0.21.0 ) require ( + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.29 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 // indirect github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/sys v0.29.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/go-kit/kit => github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 + +// //DELETE after testing +// replace "github.com/Azure/custom-script-extension-linux" => "../custom-script-extension-linux" + diff --git a/go.sum b/go.sum index 6a10423..5ac8ee9 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,27 @@ github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 h github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187/go.mod h1:a0BFq9UoWBHvBS7iagvjFqBjYfxtBsmqvCLWIHRq9b0= github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954 h1:Jnedpc6riCirNEUQGZ7I7gH1PZym71sKFnbP9Lb32oA= github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954/go.mod h1:1hEkO8M1zN/SQpdFOTDDMTNfeE1Q2tCHmEXXiHrWTgo= +github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6 h1:RDJpiDBkLFa711zwULKd1bhIoWpaslIwlfz5CBHn+eI= +github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY= github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible h1:7CctKV2SGUVFq3a+WNHypGRKQzaPCNVEAMMAlEJXrWc= github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4= +github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 h1:/sPElPBMLSi6+bV0o0fPN4U24qQCNHs1i/BjnO+GqLc= github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1/go.mod h1:Rg55S63lgqSBCawY/oTm7jdFSySp6jwIqgHMB2IeHK8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,8 +34,16 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 h1:3nOfQt8sRPYbXORD5tJ8YyQ3HlL2Jt3LJ2U17CbNh6I= github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b h1:OFvZV3a+25cGJH9dETHw0nk0wV6hLZI7IJijOkXEFS0= @@ -45,8 +72,60 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/main/cmds.go b/main/cmds.go index 354f815..8ef85fd 100644 --- a/main/cmds.go +++ b/main/cmds.go @@ -13,6 +13,8 @@ import ( "time" utils "github.com/Azure/azure-extension-platform/pkg/utils" + vmextension "github.com/Azure/azure-extension-platform/vmextension" + github.com/Azure/custom-script-extension-linux/pkg/errorutil "github.com/Azure/custom-script-extension-linux/pkg/seqnum" "github.com/go-kit/kit/log" "github.com/pkg/errors" @@ -22,7 +24,7 @@ const ( maxScriptSize = 256 * 1024 ) -type cmdFunc func(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) (msg string, err error) +type cmdFunc func(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) (msg string, ewc vmextension.ErrorWithClarification) type preFunc func(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) error type cmd struct { @@ -55,14 +57,14 @@ var ( } ) -func noop(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) { +func noop(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextension.ErrorWithClarification) { ctx.Log("event", "noop") - return "", nil + return "", vmextension.NewErrorWithClarification(errorutil.noError, nil) } -func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) { +func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextension.ErrorWithClarification) { if err := os.MkdirAll(dataDir, 0755); err != nil { - return "", errors.Wrap(err, "failed to create data dir") + return "", vmextension.NewErrorWithClarification(errorutil.systemError, errors.Wrap(err, "failed to create data dir")) } // If the file mrseq does not exists it is for two possible reasons. @@ -74,10 +76,10 @@ func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) ctx.Log("event", "created data dir", "path", dataDir) ctx.Log("event", "installed") - return "", nil + return "", vmextension.NewErrorWithClarification(errorutil.noError, nil) } -func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) { +func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextensionErrorWithClarification) { { // a new context scope with path ctx = ctx.With("path", dataDir) ctx.Log("event", "removing data dir", "path", dataDir) @@ -87,7 +89,7 @@ func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, erro ctx.Log("event", "removed data dir") } ctx.Log("event", "uninstalled") - return "", nil + return "", vmextension.NewErrorWithClarification(errorutil.noError, nil) } func enablePre(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) error { @@ -110,16 +112,18 @@ func min(a, b int) int { return b } -func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) { +func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextensionErrorWithClarification) { // parse the extension handler settings (not available prior to 'enable') - cfg, err := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder, seqNum) - if err != nil { - return "", errors.Wrap(err, "failed to get configuration") + cfg, ewc := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder, seqNum) + if ewc.Err != nil { + ewc.Err = errors.Wrap(ewc.Err, "failed to get configuration") + return "", ewc } dir := filepath.Join(dataDir, downloadDir, fmt.Sprintf("%d", seqNum)) - if err := downloadFiles(ctx, dir, cfg); err != nil { - return "", errors.Wrap(err, "processing file downloads failed") + if ewc := downloadFiles(ctx, dir, cfg); ewc.Err != nil { + ewc.Err = errors.Wrap(ewc.Err, "processing file downloads failed") + return "", ewc } // execute the command, save its error @@ -175,12 +179,12 @@ func checkAndSaveSeqNum(ctx log.Logger, seq int, mrseqPath string) (shouldExit b // downloadFiles downloads the files specified in cfg into dir (creates if does // not exist) and takes storage credentials specified in cfg into account. -func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) error { +func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) vmextension.ErrorWithClarification { // - prepare the output directory for files and the command output // - create the directory if missing ctx.Log("event", "creating output directory", "path", dir) if err := os.MkdirAll(dir, 0700); err != nil { - return errors.Wrap(err, "failed to prepare output directory") + return vmextension.NewErrorWithClarification(errorutil.fileDownload_unableToCreateDownloadDirectory, errors.Wrap(err, "failed to prepare output directory")) } ctx.Log("event", "created output directory") @@ -200,13 +204,13 @@ func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) error { for i, f := range cfg.fileUrls() { ctx := ctx.With("file", i) ctx.Log("event", "download start") - if err := downloadAndProcessURL(ctx, f, dir, &cfg); err != nil { - ctx.Log("event", "download failed", "error", err) - return errors.Wrapf(err, "failed to download file[%d]", i) + if ewc := downloadAndProcessURL(ctx, f, dir, &cfg); ewc.Err != nil { + ctx.Log("event", "download failed", "error", ewc.Err) + return vmextension.NewErrorWithClarification(ewc.ErrorCode, errors.Wrapf(ewc.Err, "failed to download file[%d]", i)) } ctx.Log("event", "download complete", "output", dir) } - return nil + return vmextension.NewErrorWithClarification(errorutil.noError, nil) } // runCmd runs the command (extracted from cfg) in the given dir (assumed to exist). diff --git a/main/exec.go b/main/exec.go index ed367ca..235c06b 100644 --- a/main/exec.go +++ b/main/exec.go @@ -9,6 +9,9 @@ import ( "syscall" "github.com/pkg/errors" + vmextension "github.com/Azure/azure-extension-platform/vmextension" + github.com/Azure/custom-script-extension-linux/pkg/errorutil + ) // Exec runs the given cmd in /bin/sh, saves its stdout/stderr streams to @@ -16,7 +19,7 @@ import ( // // On error, an exit code may be returned if it is an exit code error. // Given stdout and stderr will be closed upon returning. -func Exec(cmd, workdir string, stdout, stderr io.WriteCloser) (int, error) { +func Exec(cmd, workdir string, stdout, stderr io.WriteCloser) (int, vmextension.ErrorWithClarification) { defer stdout.Close() defer stderr.Close() @@ -30,6 +33,9 @@ func Exec(cmd, workdir string, stdout, stderr io.WriteCloser) (int, error) { if ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { code := status.ExitStatus() + if code != 0 { + return code, vmextension.NewErrorWithClarification(errorutil.commandExecution_failureExitCode, fmt.Errorf("command terminated with exit status=%d", code)) + } return code, fmt.Errorf("command terminated with exit status=%d", code) } } diff --git a/main/files.go b/main/files.go index 8e57090..3bf686f 100644 --- a/main/files.go +++ b/main/files.go @@ -9,48 +9,53 @@ import ( "os" + "github.com/Azure/azure-extension-platform/vmextension" "github.com/Azure/custom-script-extension-linux/pkg/blobutil" "github.com/Azure/custom-script-extension-linux/pkg/download" "github.com/Azure/custom-script-extension-linux/pkg/preprocess" "github.com/Azure/custom-script-extension-linux/pkg/urlutil" "github.com/go-kit/kit/log" "github.com/pkg/errors" + + github.com/Azure/custom-script-extension-linux/pkg/errorutil + ) // downloadAndProcessURL downloads using the specified downloader and saves it to the // specified existing directory, which must be the path to the saved file. Then // it post-processes file based on heuristics. -func downloadAndProcessURL(ctx *log.Context, url, downloadDir string, cfg *handlerSettings) error { +func downloadAndProcessURL(ctx *log.Context, url, downloadDir string, cfg *handlerSettings) vmextension.ErrorWithClarification { fn, err := urlToFileName(url) if err != nil { - return err + return vmextension.NewErrorWithClarification(errorutil.customerInput_invalidFileUris, err) } if !urlutil.IsValidUrl(url) { - return fmt.Errorf("[REDACTED] is not a valid url") + return vmextension.NewErrorWithClarification(errorutil.customerInput_invalidFileUris, + fmt.Errorf("[REDACTED] is not a valid url")) } - dl, err := getDownloaders(url, cfg.StorageAccountName, cfg.StorageAccountKey, cfg.ManagedIdentity) - if err != nil { - return err + dl, ewc := getDownloaders(url, cfg.StorageAccountName, cfg.StorageAccountKey, cfg.ManagedIdentity) + if ewc.Err != nil { + return ewc } fp := filepath.Join(downloadDir, fn) const mode = 0500 // we assume users download scripts to execute - if _, err := download.SaveTo(ctx, dl, fp, mode); err != nil { - return err + if _, ewc := download.SaveTo(ctx, dl, fp, mode); ewc.Err != nil { + return ewc } if cfg.SkipDos2Unix == false { err = postProcessFile(fp) } - return errors.Wrapf(err, "failed to post-process '%s'", fn) + return vmextension.NewErrorWithClarification(errorutil.systemError, errors.Wrapf(err, "failed to post-process '%s'", fn)) } // getDownloader returns a downloader for the given URL based on whether the // storage credentials are empty or not. func getDownloaders(fileURL string, storageAccountName, storageAccountKey string, managedIdentity *clientOrObjectId) ( - []download.Downloader, error) { + []download.Downloader, vmextension.ErrorWithClarification) { if storageAccountName == "" || storageAccountKey == "" { // storage account name and key cannot be specified with managed identity, handler settings validation won't allow that // handler settings validation will also not allow storageAccountName XOR storageAccountKey == 1 @@ -67,27 +72,27 @@ func getDownloaders(fileURL string, storageAccountName, storageAccountKey string case managedIdentity.ClientId == "" && managedIdentity.ObjectId != "": msiProvider = download.GetMsiProviderForStorageAccountsWithObjectId(fileURL, managedIdentity.ObjectId) default: - return nil, fmt.Errorf("unexpected combination of ClientId and ObjectId found") + return nil, vmextension.NewErrorWithClarification(errorutil.customerInput_clientIdObjectIdBothSpecified, fmt.Errorf("unexpected combination of ClientId and ObjectId found")) } return []download.Downloader{ // try downloading without MSI token first, but attempt with MSI if the download fails download.NewURLDownload(fileURL), download.NewBlobWithMsiDownload(fileURL, msiProvider), - }, nil + }, vmextension.NewErrorWithClarification(errorutil.noError, nil) } else { // do not use MSI downloader if the uri is not azure storage blob, or managedIdentity isn't specified - return []download.Downloader{download.NewURLDownload(fileURL)}, nil + return []download.Downloader{download.NewURLDownload(fileURL)}, vmextension.NewErrorWithClarification(errorutil.noError, nil) } } else { // if storage name account and key are specified, use that for all files // this preserves old behavior blob, err := blobutil.ParseBlobURL(fileURL) if err != nil { - return nil, err + return nil, vmextension.NewErrorWithClarification(errorutil.customerInput_invalidFileUris, err) } return []download.Downloader{download.NewBlobDownload( - storageAccountName, storageAccountKey, - blob)}, nil + storageAccountName, storageAccountKey, blob)}, + vmextension.NewErrorWithClarification(errorutil.noError, nil) } } diff --git a/main/handlersettings.go b/main/handlersettings.go index a15dee5..c23ad8f 100644 --- a/main/handlersettings.go +++ b/main/handlersettings.go @@ -7,12 +7,15 @@ import ( "github.com/go-kit/kit/log" "github.com/pkg/errors" + "github.com/Azure/azure-extension-platform/vmextension" + github.com/Azure/custom-script-extension-linux/pkg/errorutil ) var ( errStoragePartialCredentials = errors.New("both 'storageAccountName' and 'storageAccountKey' must be specified") errCmdTooMany = errors.New("'commandToExecute' was specified both in public and protected settings; it must be specified only once") errScriptTooMany = errors.New("'script' was specified both in public and protected settings; it must be specified only once") + errFileUrisTooMany = errors.New("'fileUris' were specified both in public and protected settings; it must be specified only once") errCmdAndScript = errors.New("'commandToExecute' and 'script' were both specified, but only one is validate at a time") errCmdMissing = errors.New("'commandToExecute' is not specified") errUsingBothKeyAndMsi = errors.New("'storageAccountName' or 'storageAccountKey' must not be specified with 'managedServiceIdentity'") @@ -48,34 +51,38 @@ func (s *handlerSettings) fileUrls() []string { // validate makes logical validation on the handlerSettings which already passed // the schema validation. -func (h handlerSettings) validate() error { +func (h handlerSettings) validate() vmextension.ErrorWithClarification { if h.commandToExecute() == "" && h.script() == "" { - return errCmdMissing + return vmextension.NewErrorWithClarification(errorutil.customerInput_commandToExecuteAndScriptNotSpecified, errCmdMissing) } if h.publicSettings.CommandToExecute != "" && h.protectedSettings.CommandToExecute != "" { - return errCmdTooMany + return vmextension.NewErrorWithClarification(errorutil.customerInput_commandToExecuteSpecifiedInTwoPlaces, errCmdTooMany) } if h.publicSettings.Script != "" && h.protectedSettings.Script != "" { - return errScriptTooMany + return vmextension.NewErrorWithClarification(errorutil.customerInput_scriptSpecifiedInTwoPlaces, errScriptTooMany) + } + + if (h.publicSettings.FileURLs != nil && len(h.publicSettings.FileURLs) > 0) && (h.protectedSettings.FileURLs != nil && len(h.privateSettings.FileURLs) > 0) { + return vmextension.NewErrorWithClarification(errorutil.customerInput_fileUrisSpecifiedInTwoPlaces, errFileUrisTooMany) } if h.commandToExecute() != "" && h.script() != "" { - return errCmdAndScript + return vmextension.NewErrorWithClarification(errorutil.customerInput_commandToExecuteAndScriptBothSpecified, errCmdAndScript) } if (h.protectedSettings.StorageAccountName != "") != (h.protectedSettings.StorageAccountKey != "") { - return errStoragePartialCredentials + return vmextension.NewErrorWithClarification(errorutil.customerInput_incompleteStorageCreds, errStoragePartialCredentials) } if (h.protectedSettings.StorageAccountKey != "" || h.protectedSettings.StorageAccountName != "") && h.protectedSettings.ManagedIdentity != nil { - return errUsingBothKeyAndMsi + return vmextension.NewErrorWithClarification(errorutil.customerInput_storageCredsAndMIBothSpecified, errUsingBothKeyAndMsi) } if h.protectedSettings.ManagedIdentity != nil { if h.protectedSettings.ManagedIdentity.ClientId != "" && h.protectedSettings.ManagedIdentity.ObjectId != "" { - return errUsingBothClientIdAndObjectId + return vmextension.NewErrorWithClarification(errorutil.customerInput_clientIdObjectIdBothSpecified, errUsingBothClientIdAndObjectId) } } @@ -113,32 +120,33 @@ func (self *clientOrObjectId) isEmpty() bool { // parseAndValidateSettings reads configuration from configFolder, decrypts it, // runs JSON-schema and logical validation on it and returns it back. -func parseAndValidateSettings(ctx *log.Context, configFolder string, seqNum int) (h handlerSettings, _ error) { +func parseAndValidateSettings(ctx *log.Context, configFolder string, seqNum int) (h handlerSettings, _ vmextension.ErrorWithClarification) { ctx.Log("event", "reading configuration") pubJSON, protJSON, err := readSettings(configFolder, seqNum) if err != nil { - return h, err + return h, vmextension.NewErrorWithClarification(errorutil.internal_badConfig, err) } ctx.Log("event", "read configuration") ctx.Log("event", "validating json schema") if err := validateSettingsSchema(pubJSON, protJSON); err != nil { - return h, errors.Wrap(err, "json validation error") + return h, vmextension.NewErrorWithClarification(errorutil.internal_badConfig, errors.Wrap(err, "json validation error")) } ctx.Log("event", "json schema valid") ctx.Log("event", "parsing configuration json") if err := UnmarshalHandlerSettings(pubJSON, protJSON, &h.publicSettings, &h.protectedSettings); err != nil { - return h, errors.Wrap(err, "json parsing error") + return h, vmextension.NewErrorWithClarification(errorutil.internal_badConfig, errors.Wrap(err, "json parsing error")) } ctx.Log("event", "parsed configuration json") ctx.Log("event", "validating configuration logically") - if err := h.validate(); err != nil { - return h, errors.Wrap(err, "invalid configuration") + if ewc := h.validate(); err != nil { + ewc.Err = errors.Wrap(ewc.Err, "invalid configuration") + return h, ewc } ctx.Log("event", "validated configuration") - return h, nil + return h, vmextension.NewErrorWithClarification(errorutil.noError, nil) } // readSettings uses specified configFolder (comes from HandlerEnvironment) to diff --git a/main/main.go b/main/main.go index cad6dfe..990e5ad 100644 --- a/main/main.go +++ b/main/main.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/go-kit/kit/log" + "github.com/pkg/errors" ) var ( @@ -78,10 +79,11 @@ func main() { } // execute the subcommand reportStatus(ctx, hEnv, seqNum, StatusTransitioning, cmd, "") - msg, err := cmd.f(ctx, hEnv, seqNum) - if err != nil { - ctx.Log("event", "failed to handle", "error", err) - reportStatus(ctx, hEnv, seqNum, StatusError, cmd, err.Error()+msg) + msg, ewc := cmd.f(ctx, hEnv, seqNum) + if ewc.Err != nil { + ctx.Log("event", "failed to handle", "error", ewc.Error()) + ewc.Err = errors.Wrap(ewc.Err, ewc.Error()+msg) + reportErrorStatus(ctx, hEnv, seqNum, StatusError, cmd, ewc) os.Exit(cmd.failExitCode) } reportStatus(ctx, hEnv, seqNum, StatusSuccess, cmd, msg) diff --git a/main/status.go b/main/status.go index 196feb4..5505598 100644 --- a/main/status.go +++ b/main/status.go @@ -8,6 +8,8 @@ import ( "path/filepath" "time" + status "github.com/Azure/azure-extension-platform/pkg/status" + vmextension "github.com/Azure/azure-extension-platform/vmextension" "github.com/go-kit/kit/log" "github.com/pkg/errors" ) @@ -102,6 +104,28 @@ func reportStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t Type, return nil } +// reportStatus saves operation status to the status file for the extension +// handler with the optional given message, if the given cmd requires reporting +// status. +// +// If an error occurs reporting the status, it will be logged and returned. +func reportErrorStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t Type, c cmd, err vmextension.ErrorWithClarification) error { + if !c.shouldReportStatus { + ctx.Log("status", "not reported for operation (by design)") + return nil + } + ewc := status.ErrorClarification{ + Code: err.ErrorCode, + Message: err.Error(), + } + s := status.NewError(c.name, ewc) + if err := s.Save(hEnv.HandlerEnvironment.StatusFolder, uint(seqNum)); err != nil { + ctx.Log("event", "failed to save handler status", "error", err) + return errors.Wrap(err, "failed to save handler status") + } + return nil +} + // readStatus loads current status file in StatusReport func readStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) (Type, error) { fileName := fmt.Sprintf("%d.status", seqNum) diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go index cbec58a..39143fc 100644 --- a/pkg/download/downloader.go +++ b/pkg/download/downloader.go @@ -6,7 +6,10 @@ import ( "net" "net/http" "time" + "url" + "github.com/Azure/azure-extension-platform/vmextension" + github.com/Azure/custom-script-extension-linux/pkg/errorutil "github.com/Azure/custom-script-extension-linux/pkg/urlutil" "github.com/go-kit/kit/log" @@ -44,10 +47,11 @@ var ( // Download retrieves a response body and checks the response status code to see // if it is 200 OK and then returns the response body. It issues a new request // every time called. It is caller's responsibility to close the response body. -func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, error) { +func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, vmextension.ErrorWithClarification) { req, err := d.GetRequest() if err != nil { - return -1, nil, errors.Wrapf(err, "failed to create http request") + return -1, nil, vmextension.NewErrorWithClarification(errorutil.fileDownload_genericError, + errors.Wrapf(err, "failed to create http request")) } requestID := req.Header.Get(xMsClientRequestIdHeaderName) if len(requestID) > 0 { @@ -55,23 +59,35 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, error) { } resp, err := httpClient.Do(req) if err != nil { + if ((url.Error)err.Timeout()) { + err = urlutil.RemoveUrlFromErr(err) + return -1, nil, vmextension.NewErrorWithClarification(errorutil.fileDownload_exceededTimeout, + errors.Wrapf(err, "http request timed out")) + } err = urlutil.RemoveUrlFromErr(err) - return -1, nil, errors.Wrapf(err, "http request failed") + return -1, nil, vmextension.NewErrorWithClarification(errorutil.fileDownload_unknownError, + errors.Wrapf(err, "http request failed")) } if resp.StatusCode == http.StatusOK { - return resp.StatusCode, resp.Body, nil + // We're setting the errorCode to MaxInt because we're only checking whether the internal error is nil + return resp.StatusCode, resp.Body, vmextension.NewErrorWithClarification(errorutil.noError, nil) } errString := "" + errClarificationCode := 0 requestId := resp.Header.Get(xMsServiceRequestIdHeaderName) switch d.(type) { case *blobWithMsiToken: switch resp.StatusCode { case http.StatusNotFound: errString = MsiDownload404ErrorString + errClarificationCode = errorutil.msi_notFound case http.StatusForbidden: errString = MsiDownload403ErrorString + errClarificationCode = errorutil.msi_doesNotHaveRightPermissions + default: + errClarificationCode = errorutil.msi_GenericRetrievalError } break default: @@ -81,28 +97,33 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, error) { errString = fmt.Sprintf("CustomScript failed to download the file from %s because access was denied. Please fix the blob permissions and try again, the response code and message returned were: %q", hostname, resp.Status) + errClarificationCode = errorutil.fileDownload_accessDenied case http.StatusNotFound: errString = fmt.Sprintf("CustomScript failed to download the file from %s because it does not exist. Please create the blob and try again, the response code and message returned were: %q", hostname, resp.Status) + errClarificationCode = errorutil.fileDownload_doesNotExist case http.StatusBadRequest: errString = fmt.Sprintf("CustomScript failed to download the file from %s because parts of the request were incorrectly formatted, missing, and/or invalid. The response code and message returned were: %q", hostname, resp.Status) + errClarificationCode = errorutil.fileDownload_badRequest case http.StatusInternalServerError: errString = fmt.Sprintf("CustomScript failed to download the file from %s due to an issue with storage. The response code and message returned were: %q", hostname, resp.Status) + errClarificationCode = errorutil.storage_internalServerError default: errString = fmt.Sprintf("CustomScript failed to download the file from %s because the server returned a response code and message of %q Please verify the machine has network connectivity.", hostname, resp.Status) + errClarificationCode = errorutil.fileDownload_networkingError } } if len(requestId) > 0 { errString += fmt.Sprintf(" (Service request ID: %s)", requestId) } - return resp.StatusCode, nil, fmt.Errorf(errString) + return resp.StatusCode, nil, vmextension.NewErrorWithClarification(errClarificationCode, fmt.Errorf(errString)) } diff --git a/pkg/download/retry.go b/pkg/download/retry.go index 6999119..f248064 100644 --- a/pkg/download/retry.go +++ b/pkg/download/retry.go @@ -8,7 +8,11 @@ import ( "os" "time" - "github.com/go-kit/kit/log" + "github.com/Azure/azure-extension-platform/vmextension" + "github.com/go-kit/kit/log" + + github.com/Azure/custom-script-extension-linux/pkg/errorutil + ) // SleepFunc pauses the execution for at least duration d. @@ -33,17 +37,17 @@ const ( // closed on failures). If the retries do not succeed, the last error is returned. // // It sleeps in exponentially increasing durations between retries. -func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf SleepFunc) (int64, error) { - var lastErr error +func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf SleepFunc) (int64, vmextension.ErrorWithClarification) { + var lastErr vmextension.ErrorWithClarification for _, d := range downloaders { for n := 0; n < expRetryN; n++ { ctx := ctx.With("retry", n) // reset the last error before each retry - lastErr = nil + lastErr = vmextension.NewErrorWithClarification(errorutil.noError, nil) start := time.Now() status, out, err := Download(ctx, d) - if err == nil { + if err.Err == nil { // server returned status code 200 OK // we have a response body, copy it to the file nBytes, innerErr := io.CopyBuffer(f, out, make([]byte, writeBufSize)) @@ -53,7 +57,7 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee out.Close() end := time.Since(start) ctx.Log("info", fmt.Sprintf("file download sucessful: downloaded and saved %d bytes in %d milliseconds", nBytes, end.Milliseconds())) - return nBytes, nil + return nBytes, vmextension.NewErrorWithClarification(errorutil.noError, nil) } else { // we failed to download the response body and write it to file // because either connection was closed prematurely or file write operation failed @@ -62,7 +66,7 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee // clear out the contents of the file so as to not leave a partial file f.Truncate(0) // cache the inner error - lastErr = innerErr + lastErr = vmextension.NewErrorWithClarification(errorutil.fileDownload_genericError, innerErr) } } else { // cache the outer error diff --git a/pkg/download/save.go b/pkg/download/save.go index 7273138..f45c46d 100644 --- a/pkg/download/save.go +++ b/pkg/download/save.go @@ -3,25 +3,27 @@ package download import ( "os" + "github.com/Azure/azure-extension-platform/vmextension" "github.com/go-kit/kit/log" "github.com/pkg/errors" + github.com/Azure/custom-script-extension-linux/pkg/errorutil ) // SaveTo uses given downloader to fetch the resource with retries and saves the // given file. Directory of dst is not created by this function. If a file at // dst exists, it will be truncated. If a new file is created, mode is used to // set the permission bits. Written number of bytes are returned on success. -func SaveTo(ctx *log.Context, d []Downloader, dst string, mode os.FileMode) (int64, error) { +func SaveTo(ctx *log.Context, d []Downloader, dst string, mode os.FileMode) (int64, vmextension.ErrorWithClarification) { f, err := os.OpenFile(dst, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, mode) if err != nil { - return 0, errors.Wrap(err, "failed to open file for writing") + return 0, vmextension.NewErrorWithClarification(errorutil.unknownError, errors.Wrap(err, "failed to open file for writing")) } defer f.Close() - n, err := WithRetries(ctx, f, d, ActualSleep) - if err != nil { - return n, errors.Wrapf(err, "failed to download response and write to file: %s", dst) + n, ewc := WithRetries(ctx, f, d, ActualSleep) + if ewc.Err != nil { + return n, vmextension.NewErrorWithClarification(ewc.ErrorCode, errors.Wrapf(ewc.Err, "failed to download response and write to file: %s", dst)) } - return n, nil + return n, vmextension.NewErrorWithClarification(errorutil.noError, nil) } diff --git a/pkg/errorutil/errorclarificationcodes.go b/pkg/errorutil/errorclarificationcodes.go index cad4274..a501c1b 100644 --- a/pkg/errorutil/errorclarificationcodes.go +++ b/pkg/errorutil/errorclarificationcodes.go @@ -22,14 +22,16 @@ const ( commandExecution_failureExitCode int = 2 commandExecution_interruptedByVmShutdown int = 3 - customerInput_commandToExecuteSpecifiedInTwoPlaces int = 20 - customerInput_ignoreRelativePathForFileDownloadsSpecifiedInTwoPlaces int = 21 - customerInput_fileUrisSpecifiedInTwoPlaces int = 22 - customerInput_commandToExecuteNotSpecified int = 23 - customerInput_fileUriContainsNull int = 24 - customerInput_invalidFileUris int = 25 - customerInput_storageCredsAndMIBothSpecified int = 26 - customerInput_clientIdObjectIdBothSpecified int = 27 + customerInput_commandToExecuteSpecifiedInTwoPlaces int = 20 + customerInput_fileUrisSpecifiedInTwoPlaces int = 22 + customerInput_commandToExecuteAndScriptNotSpecified int = 23 + customerInput_fileUriContainsNull int = 24 + customerInput_invalidFileUris int = 25 + customerInput_storageCredsAndMIBothSpecified int = 26 + customerInput_clientIdObjectIdBothSpecified int = 27 + customerInput_scriptSpecifiedInTwoPlaces int = 28 + customerInput_commandToExecuteAndScriptBothSpecified int = 29 + customerInput_incompleteStorageCreds int = 30 fileDownload_unableToCreateDownloadDirectory int = 50 fileDownload_sasExpired int = 51 From ba346da4299b545d1fbd23fff86bbde2641a4591 Mon Sep 17 00:00:00 2001 From: Deepti Vaidyanathan Date: Wed, 8 Jan 2025 23:43:52 +0000 Subject: [PATCH 3/4] Fixes error clarification codes --- go.mod | 27 +++++----- go.sum | 38 +++++++------ main/cmds.go | 39 +++++++------- main/exec.go | 26 ++++----- main/files.go | 18 +++---- main/handlersettings.go | 30 +++++------ pkg/download/downloader.go | 38 ++++++------- pkg/download/downloader_test.go | 33 ++++++++---- pkg/download/retry.go | 24 +++++---- pkg/download/retry_test.go | 10 ++-- pkg/download/save.go | 15 +++--- pkg/errorutil/errorclarificationcodes.go | 69 ++++++++++++------------ 12 files changed, 192 insertions(+), 175 deletions(-) diff --git a/go.mod b/go.mod index 44f9d88..4836129 100644 --- a/go.mod +++ b/go.mod @@ -1,47 +1,48 @@ module github.com/Azure/custom-script-extension-linux -go 1.23 - -toolchain go1.23.4 +go 1.23.4 require ( - github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6 - github.com/Azure/azure-sdk-for-go v68.0.0+incompatible - github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 + github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb github.com/go-kit/kit v0.13.0 - github.com/google/uuid v1.6.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/text v0.21.0 ) require ( + github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 // indirect + github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-kit/log v0.2.1 github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang-jwt/jwt/v4 v4.5.1 // indirect - github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 // indirect - github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect golang.org/x/crypto v0.32.0 // indirect golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v2 v2.2.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/go-kit/kit => github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 - // //DELETE after testing -// replace "github.com/Azure/custom-script-extension-linux" => "../custom-script-extension-linux" +//require github.com/Azure/custom-script-extension-linux/pkg v0.0.0 +//replace github.com/Azure/custom-script-extension-linux/pkg => ../custom-script-extension-linux/pkg + +replace github.com/go-kit/kit => github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 diff --git a/go.sum b/go.sum index 5ac8ee9..ffaaa0a 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,7 @@ github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 h1:C4S32XsUvctWzdWDEYlvhfcgH1iGvSD62II7Dd7F6B8= github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187/go.mod h1:a0BFq9UoWBHvBS7iagvjFqBjYfxtBsmqvCLWIHRq9b0= -github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954 h1:Jnedpc6riCirNEUQGZ7I7gH1PZym71sKFnbP9Lb32oA= -github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954/go.mod h1:1hEkO8M1zN/SQpdFOTDDMTNfeE1Q2tCHmEXXiHrWTgo= github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6 h1:RDJpiDBkLFa711zwULKd1bhIoWpaslIwlfz5CBHn+eI= github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY= -github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible h1:7CctKV2SGUVFq3a+WNHypGRKQzaPCNVEAMMAlEJXrWc= -github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -18,18 +14,29 @@ github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2 github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 h1:/sPElPBMLSi6+bV0o0fPN4U24qQCNHs1i/BjnO+GqLc= -github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1/go.mod h1:Rg55S63lgqSBCawY/oTm7jdFSySp6jwIqgHMB2IeHK8= +github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb h1:DSAtit+Eq3K2/kzZsSBYSh3jfCBiWm+FMJkFF0Ffy+I= +github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb/go.mod h1:Rg55S63lgqSBCawY/oTm7jdFSySp6jwIqgHMB2IeHK8= +github.com/ahmetb/go-httpbin v0.0.0-20240315150752-da45896c98cb h1:od9/PvyZ6X+dCU04fTRyrYS8HKVW1SxprFvLYjzUI0U= +github.com/ahmetb/go-httpbin v0.0.0-20240315150752-da45896c98cb/go.mod h1:iB3NbHoh0P/9AZepPBcH+gM1PhQJGmsres+ZHf72M3k= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 h1:awXynDTA1TiAp1SA/o/xoU6oRHE3xKCokck9l4/poMc= github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= @@ -40,14 +47,10 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 h1:3nOfQt8sRPYbXORD5tJ8YyQ3HlL2Jt3LJ2U17CbNh6I= -github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b h1:OFvZV3a+25cGJH9dETHw0nk0wV6hLZI7IJijOkXEFS0= -github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -72,14 +75,14 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -90,6 +93,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -102,8 +107,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -116,7 +119,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= @@ -129,6 +131,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main/cmds.go b/main/cmds.go index 8ef85fd..d118505 100644 --- a/main/cmds.go +++ b/main/cmds.go @@ -14,7 +14,7 @@ import ( utils "github.com/Azure/azure-extension-platform/pkg/utils" vmextension "github.com/Azure/azure-extension-platform/vmextension" - github.com/Azure/custom-script-extension-linux/pkg/errorutil + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/Azure/custom-script-extension-linux/pkg/seqnum" "github.com/go-kit/kit/log" "github.com/pkg/errors" @@ -59,12 +59,12 @@ var ( func noop(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextension.ErrorWithClarification) { ctx.Log("event", "noop") - return "", vmextension.NewErrorWithClarification(errorutil.noError, nil) + return "", vmextension.NewErrorWithClarification(errorutil.NoError, nil) } func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextension.ErrorWithClarification) { if err := os.MkdirAll(dataDir, 0755); err != nil { - return "", vmextension.NewErrorWithClarification(errorutil.systemError, errors.Wrap(err, "failed to create data dir")) + return "", vmextension.NewErrorWithClarification(errorutil.SystemError, errors.Wrap(err, "failed to create data dir")) } // If the file mrseq does not exists it is for two possible reasons. @@ -76,20 +76,20 @@ func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmexte ctx.Log("event", "created data dir", "path", dataDir) ctx.Log("event", "installed") - return "", vmextension.NewErrorWithClarification(errorutil.noError, nil) + return "", vmextension.NewErrorWithClarification(errorutil.NoError, nil) } -func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextensionErrorWithClarification) { +func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextension.ErrorWithClarification) { { // a new context scope with path ctx = ctx.With("path", dataDir) ctx.Log("event", "removing data dir", "path", dataDir) if err := os.RemoveAll(dataDir); err != nil { - return "", errors.Wrap(err, "failed to delete data directory") + return "", vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrap(err, "failed to delete data directory")) } ctx.Log("event", "removed data dir") } ctx.Log("event", "uninstalled") - return "", vmextension.NewErrorWithClarification(errorutil.noError, nil) + return "", vmextension.NewErrorWithClarification(errorutil.NoError, nil) } func enablePre(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) error { @@ -112,7 +112,7 @@ func min(a, b int) int { return b } -func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextensionErrorWithClarification) { +func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmextension.ErrorWithClarification) { // parse the extension handler settings (not available prior to 'enable') cfg, ewc := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder, seqNum) if ewc.Err != nil { @@ -140,7 +140,7 @@ func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, vmexten ctx.Log("message", "error tailing stderr logs", "error", err) } - isSuccess := runErr == nil + isSuccess := runErr.Err == nil telemetry("Output", "-- stdout/stderr omitted from telemetry pipeline --", isSuccess, 0) if isSuccess { @@ -184,7 +184,7 @@ func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) vmextensio // - create the directory if missing ctx.Log("event", "creating output directory", "path", dir) if err := os.MkdirAll(dir, 0700); err != nil { - return vmextension.NewErrorWithClarification(errorutil.fileDownload_unableToCreateDownloadDirectory, errors.Wrap(err, "failed to prepare output directory")) + return vmextension.NewErrorWithClarification(errorutil.FileDownload_unableToCreateDownloadDirectory, errors.Wrap(err, "failed to prepare output directory")) } ctx.Log("event", "created output directory") @@ -210,15 +210,16 @@ func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) vmextensio } ctx.Log("event", "download complete", "output", dir) } - return vmextension.NewErrorWithClarification(errorutil.noError, nil) + return vmextension.NewErrorWithClarification(errorutil.NoError, nil) } // runCmd runs the command (extracted from cfg) in the given dir (assumed to exist). -func runCmd(ctx log.Logger, dir string, cfg handlerSettings) (err error) { +func runCmd(ctx log.Logger, dir string, cfg handlerSettings) (ewc vmextension.ErrorWithClarification) { ctx.Log("event", "executing command", "output", dir) var cmd string var scenario string var scenarioInfo string + var err error // So many ways to execute a command! if cfg.publicSettings.CommandToExecute != "" { @@ -232,30 +233,30 @@ func runCmd(ctx log.Logger, dir string, cfg handlerSettings) (err error) { } else if cfg.publicSettings.Script != "" { ctx.Log("event", "executing public script", "output", dir) if cmd, scenarioInfo, err = writeTempScript(cfg.publicSettings.Script, dir, cfg.publicSettings.SkipDos2Unix); err != nil { - return + return vmextension.NewErrorWithClarification(errorutil.NoError, err) } scenario = fmt.Sprintf("public-script;%s", scenarioInfo) } else if cfg.protectedSettings.Script != "" { ctx.Log("event", "executing protected script", "output", dir) if cmd, scenarioInfo, err = writeTempScript(cfg.protectedSettings.Script, dir, cfg.publicSettings.SkipDos2Unix); err != nil { - return + return vmextension.NewErrorWithClarification(errorutil.NoError, err) } scenario = fmt.Sprintf("protected-script;%s", scenarioInfo) } begin := time.Now() - err = ExecCmdInDir(cmd, dir) + ewc = ExecCmdInDir(cmd, dir) elapsed := time.Now().Sub(begin) - isSuccess := err == nil + isSuccess := ewc.Err == nil telemetry("scenario", scenario, isSuccess, elapsed) - if err != nil { + if ewc.Err != nil { ctx.Log("event", "failed to execute command", "error", err, "output", dir) - return errors.Wrap(err, "failed to execute command") + return vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrap(err, "failed to execute command")) } ctx.Log("event", "executed command", "output", dir) - return nil + return vmextension.NewErrorWithClarification(errorutil.NoError, nil) } func writeTempScript(script, dir string, skipDosToUnix bool) (string, string, error) { diff --git a/main/exec.go b/main/exec.go index 235c06b..9b97375 100644 --- a/main/exec.go +++ b/main/exec.go @@ -8,10 +8,9 @@ import ( "path/filepath" "syscall" - "github.com/pkg/errors" vmextension "github.com/Azure/azure-extension-platform/vmextension" - github.com/Azure/custom-script-extension-linux/pkg/errorutil - + errorutil "github.com/Azure/custom-script-extension-linux/pkg/errorutil" + "github.com/pkg/errors" ) // Exec runs the given cmd in /bin/sh, saves its stdout/stderr streams to @@ -33,13 +32,14 @@ func Exec(cmd, workdir string, stdout, stderr io.WriteCloser) (int, vmextension. if ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { code := status.ExitStatus() - if code != 0 { - return code, vmextension.NewErrorWithClarification(errorutil.commandExecution_failureExitCode, fmt.Errorf("command terminated with exit status=%d", code)) - } - return code, fmt.Errorf("command terminated with exit status=%d", code) + return code, vmextension.NewErrorWithClarification(errorutil.CommandExecution_failedUnknownError, fmt.Errorf("command terminated with exit status=%d", code)) } } - return 0, errors.Wrapf(err, "failed to execute command") + if err == nil { + return 0, vmextension.NewErrorWithClarification(errorutil.NoError, nil) + } + return 0, vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrapf(err, "failed to execute command")) + } // ExecCmdInDir executes the given command in given directory and saves output @@ -48,20 +48,20 @@ func Exec(cmd, workdir string, stdout, stderr io.WriteCloser) (int, vmextension. // // Ideally, we execute commands only once per sequence number in custom-script-extension, // and save their output under /var/lib/waagent//download//*. -func ExecCmdInDir(cmd, workdir string) error { +func ExecCmdInDir(cmd, workdir string) vmextension.ErrorWithClarification { outFn, errFn := logPaths(workdir) outF, err := os.OpenFile(outFn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { - return errors.Wrapf(err, "failed to open stdout file") + return vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrapf(err, "failed to open stdout file")) } errF, err := os.OpenFile(errFn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { - return errors.Wrapf(err, "failed to open stderr file") + return vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrapf(err, "failed to open stderr file")) } - _, err = Exec(cmd, workdir, outF, errF) - return err + _, ewc := Exec(cmd, workdir, outF, errF) + return ewc } // logPaths returns stdout and stderr file paths for the specified output diff --git a/main/files.go b/main/files.go index 3bf686f..a513642 100644 --- a/main/files.go +++ b/main/files.go @@ -17,7 +17,7 @@ import ( "github.com/go-kit/kit/log" "github.com/pkg/errors" - github.com/Azure/custom-script-extension-linux/pkg/errorutil + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" ) @@ -27,11 +27,11 @@ import ( func downloadAndProcessURL(ctx *log.Context, url, downloadDir string, cfg *handlerSettings) vmextension.ErrorWithClarification { fn, err := urlToFileName(url) if err != nil { - return vmextension.NewErrorWithClarification(errorutil.customerInput_invalidFileUris, err) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_invalidFileUris, err) } if !urlutil.IsValidUrl(url) { - return vmextension.NewErrorWithClarification(errorutil.customerInput_invalidFileUris, + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_invalidFileUris, fmt.Errorf("[REDACTED] is not a valid url")) } @@ -49,7 +49,7 @@ func downloadAndProcessURL(ctx *log.Context, url, downloadDir string, cfg *handl if cfg.SkipDos2Unix == false { err = postProcessFile(fp) } - return vmextension.NewErrorWithClarification(errorutil.systemError, errors.Wrapf(err, "failed to post-process '%s'", fn)) + return vmextension.NewErrorWithClarification(errorutil.SystemError, errors.Wrapf(err, "failed to post-process '%s'", fn)) } // getDownloader returns a downloader for the given URL based on whether the @@ -72,27 +72,27 @@ func getDownloaders(fileURL string, storageAccountName, storageAccountKey string case managedIdentity.ClientId == "" && managedIdentity.ObjectId != "": msiProvider = download.GetMsiProviderForStorageAccountsWithObjectId(fileURL, managedIdentity.ObjectId) default: - return nil, vmextension.NewErrorWithClarification(errorutil.customerInput_clientIdObjectIdBothSpecified, fmt.Errorf("unexpected combination of ClientId and ObjectId found")) + return nil, vmextension.NewErrorWithClarification(errorutil.CustomerInput_clientIdObjectIdBothSpecified, fmt.Errorf("unexpected combination of ClientId and ObjectId found")) } return []download.Downloader{ // try downloading without MSI token first, but attempt with MSI if the download fails download.NewURLDownload(fileURL), download.NewBlobWithMsiDownload(fileURL, msiProvider), - }, vmextension.NewErrorWithClarification(errorutil.noError, nil) + }, vmextension.NewErrorWithClarification(errorutil.NoError, nil) } else { // do not use MSI downloader if the uri is not azure storage blob, or managedIdentity isn't specified - return []download.Downloader{download.NewURLDownload(fileURL)}, vmextension.NewErrorWithClarification(errorutil.noError, nil) + return []download.Downloader{download.NewURLDownload(fileURL)}, vmextension.NewErrorWithClarification(errorutil.NoError, nil) } } else { // if storage name account and key are specified, use that for all files // this preserves old behavior blob, err := blobutil.ParseBlobURL(fileURL) if err != nil { - return nil, vmextension.NewErrorWithClarification(errorutil.customerInput_invalidFileUris, err) + return nil, vmextension.NewErrorWithClarification(errorutil.CustomerInput_invalidFileUris, err) } return []download.Downloader{download.NewBlobDownload( storageAccountName, storageAccountKey, blob)}, - vmextension.NewErrorWithClarification(errorutil.noError, nil) + vmextension.NewErrorWithClarification(errorutil.NoError, nil) } } diff --git a/main/handlersettings.go b/main/handlersettings.go index c23ad8f..7899cb6 100644 --- a/main/handlersettings.go +++ b/main/handlersettings.go @@ -8,7 +8,7 @@ import ( "github.com/go-kit/kit/log" "github.com/pkg/errors" "github.com/Azure/azure-extension-platform/vmextension" - github.com/Azure/custom-script-extension-linux/pkg/errorutil + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" ) var ( @@ -53,40 +53,40 @@ func (s *handlerSettings) fileUrls() []string { // the schema validation. func (h handlerSettings) validate() vmextension.ErrorWithClarification { if h.commandToExecute() == "" && h.script() == "" { - return vmextension.NewErrorWithClarification(errorutil.customerInput_commandToExecuteAndScriptNotSpecified, errCmdMissing) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_commandToExecuteAndScriptNotSpecified, errCmdMissing) } if h.publicSettings.CommandToExecute != "" && h.protectedSettings.CommandToExecute != "" { - return vmextension.NewErrorWithClarification(errorutil.customerInput_commandToExecuteSpecifiedInTwoPlaces, errCmdTooMany) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_commandToExecuteSpecifiedInTwoPlaces, errCmdTooMany) } if h.publicSettings.Script != "" && h.protectedSettings.Script != "" { - return vmextension.NewErrorWithClarification(errorutil.customerInput_scriptSpecifiedInTwoPlaces, errScriptTooMany) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_scriptSpecifiedInTwoPlaces, errScriptTooMany) } - if (h.publicSettings.FileURLs != nil && len(h.publicSettings.FileURLs) > 0) && (h.protectedSettings.FileURLs != nil && len(h.privateSettings.FileURLs) > 0) { - return vmextension.NewErrorWithClarification(errorutil.customerInput_fileUrisSpecifiedInTwoPlaces, errFileUrisTooMany) + if (h.publicSettings.FileURLs != nil && len(h.publicSettings.FileURLs) > 0) && (h.protectedSettings.FileURLs != nil && len(h.protectedSettings.FileURLs) > 0) { + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_fileUrisSpecifiedInTwoPlaces, errFileUrisTooMany) } if h.commandToExecute() != "" && h.script() != "" { - return vmextension.NewErrorWithClarification(errorutil.customerInput_commandToExecuteAndScriptBothSpecified, errCmdAndScript) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_commandToExecuteAndScriptBothSpecified, errCmdAndScript) } if (h.protectedSettings.StorageAccountName != "") != (h.protectedSettings.StorageAccountKey != "") { - return vmextension.NewErrorWithClarification(errorutil.customerInput_incompleteStorageCreds, errStoragePartialCredentials) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_incompleteStorageCreds, errStoragePartialCredentials) } if (h.protectedSettings.StorageAccountKey != "" || h.protectedSettings.StorageAccountName != "") && h.protectedSettings.ManagedIdentity != nil { - return vmextension.NewErrorWithClarification(errorutil.customerInput_storageCredsAndMIBothSpecified, errUsingBothKeyAndMsi) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_storageCredsAndMIBothSpecified, errUsingBothKeyAndMsi) } if h.protectedSettings.ManagedIdentity != nil { if h.protectedSettings.ManagedIdentity.ClientId != "" && h.protectedSettings.ManagedIdentity.ObjectId != "" { - return vmextension.NewErrorWithClarification(errorutil.customerInput_clientIdObjectIdBothSpecified, errUsingBothClientIdAndObjectId) + return vmextension.NewErrorWithClarification(errorutil.CustomerInput_clientIdObjectIdBothSpecified, errUsingBothClientIdAndObjectId) } } - return nil + return vmextension.NewErrorWithClarification(errorutil.NoError, nil) } // publicSettings is the type deserialized from public configuration section of @@ -124,19 +124,19 @@ func parseAndValidateSettings(ctx *log.Context, configFolder string, seqNum int) ctx.Log("event", "reading configuration") pubJSON, protJSON, err := readSettings(configFolder, seqNum) if err != nil { - return h, vmextension.NewErrorWithClarification(errorutil.internal_badConfig, err) + return h, vmextension.NewErrorWithClarification(errorutil.Internal_badConfig, err) } ctx.Log("event", "read configuration") ctx.Log("event", "validating json schema") if err := validateSettingsSchema(pubJSON, protJSON); err != nil { - return h, vmextension.NewErrorWithClarification(errorutil.internal_badConfig, errors.Wrap(err, "json validation error")) + return h, vmextension.NewErrorWithClarification(errorutil.Internal_badConfig, errors.Wrap(err, "json validation error")) } ctx.Log("event", "json schema valid") ctx.Log("event", "parsing configuration json") if err := UnmarshalHandlerSettings(pubJSON, protJSON, &h.publicSettings, &h.protectedSettings); err != nil { - return h, vmextension.NewErrorWithClarification(errorutil.internal_badConfig, errors.Wrap(err, "json parsing error")) + return h, vmextension.NewErrorWithClarification(errorutil.Internal_badConfig, errors.Wrap(err, "json parsing error")) } ctx.Log("event", "parsed configuration json") @@ -146,7 +146,7 @@ func parseAndValidateSettings(ctx *log.Context, configFolder string, seqNum int) return h, ewc } ctx.Log("event", "validated configuration") - return h, vmextension.NewErrorWithClarification(errorutil.noError, nil) + return h, vmextension.NewErrorWithClarification(errorutil.NoError, nil) } // readSettings uses specified configFolder (comes from HandlerEnvironment) to diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go index 39143fc..cb29f01 100644 --- a/pkg/download/downloader.go +++ b/pkg/download/downloader.go @@ -6,10 +6,9 @@ import ( "net" "net/http" "time" - "url" + "net/url" - "github.com/Azure/azure-extension-platform/vmextension" - github.com/Azure/custom-script-extension-linux/pkg/errorutil + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/Azure/custom-script-extension-linux/pkg/urlutil" "github.com/go-kit/kit/log" @@ -47,11 +46,10 @@ var ( // Download retrieves a response body and checks the response status code to see // if it is 200 OK and then returns the response body. It issues a new request // every time called. It is caller's responsibility to close the response body. -func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, vmextension.ErrorWithClarification) { +func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, int, error) { req, err := d.GetRequest() if err != nil { - return -1, nil, vmextension.NewErrorWithClarification(errorutil.fileDownload_genericError, - errors.Wrapf(err, "failed to create http request")) + return -1, nil, errorutil.FileDownload_genericError, errors.Wrapf(err, "failed to create http request") } requestID := req.Header.Get(xMsClientRequestIdHeaderName) if len(requestID) > 0 { @@ -59,19 +57,17 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, vmextension.E } resp, err := httpClient.Do(req) if err != nil { - if ((url.Error)err.Timeout()) { + if ((err.(*url.Error)).Timeout()) { err = urlutil.RemoveUrlFromErr(err) - return -1, nil, vmextension.NewErrorWithClarification(errorutil.fileDownload_exceededTimeout, - errors.Wrapf(err, "http request timed out")) + return -1, nil, errorutil.FileDownload_exceededTimeout, errors.Wrapf(err, "http request timed out") } err = urlutil.RemoveUrlFromErr(err) - return -1, nil, vmextension.NewErrorWithClarification(errorutil.fileDownload_unknownError, - errors.Wrapf(err, "http request failed")) + return -1, nil, errorutil.FileDownload_unknownError, errors.Wrapf(err, "http request failed") } if resp.StatusCode == http.StatusOK { // We're setting the errorCode to MaxInt because we're only checking whether the internal error is nil - return resp.StatusCode, resp.Body, vmextension.NewErrorWithClarification(errorutil.noError, nil) + return resp.StatusCode, resp.Body, errorutil.NoError, nil } errString := "" @@ -82,12 +78,12 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, vmextension.E switch resp.StatusCode { case http.StatusNotFound: errString = MsiDownload404ErrorString - errClarificationCode = errorutil.msi_notFound + errClarificationCode = errorutil.Msi_notFound case http.StatusForbidden: errString = MsiDownload403ErrorString - errClarificationCode = errorutil.msi_doesNotHaveRightPermissions + errClarificationCode = errorutil.Msi_doesNotHaveRightPermissions default: - errClarificationCode = errorutil.msi_GenericRetrievalError + errClarificationCode = errorutil.Msi_GenericRetrievalError } break default: @@ -97,33 +93,33 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, vmextension.E errString = fmt.Sprintf("CustomScript failed to download the file from %s because access was denied. Please fix the blob permissions and try again, the response code and message returned were: %q", hostname, resp.Status) - errClarificationCode = errorutil.fileDownload_accessDenied + errClarificationCode = errorutil.FileDownload_accessDenied case http.StatusNotFound: errString = fmt.Sprintf("CustomScript failed to download the file from %s because it does not exist. Please create the blob and try again, the response code and message returned were: %q", hostname, resp.Status) - errClarificationCode = errorutil.fileDownload_doesNotExist + errClarificationCode = errorutil.FileDownload_doesNotExist case http.StatusBadRequest: errString = fmt.Sprintf("CustomScript failed to download the file from %s because parts of the request were incorrectly formatted, missing, and/or invalid. The response code and message returned were: %q", hostname, resp.Status) - errClarificationCode = errorutil.fileDownload_badRequest + errClarificationCode = errorutil.FileDownload_badRequest case http.StatusInternalServerError: errString = fmt.Sprintf("CustomScript failed to download the file from %s due to an issue with storage. The response code and message returned were: %q", hostname, resp.Status) - errClarificationCode = errorutil.storage_internalServerError + errClarificationCode = errorutil.Storage_internalServerError default: errString = fmt.Sprintf("CustomScript failed to download the file from %s because the server returned a response code and message of %q Please verify the machine has network connectivity.", hostname, resp.Status) - errClarificationCode = errorutil.fileDownload_networkingError + errClarificationCode = errorutil.FileDownload_networkingError } } if len(requestId) > 0 { errString += fmt.Sprintf(" (Service request ID: %s)", requestId) } - return resp.StatusCode, nil, vmextension.NewErrorWithClarification(errClarificationCode, fmt.Errorf(errString)) + return resp.StatusCode, nil, errClarificationCode, fmt.Errorf(errString) } diff --git a/pkg/download/downloader_test.go b/pkg/download/downloader_test.go index 7c2bf2d..7657154 100644 --- a/pkg/download/downloader_test.go +++ b/pkg/download/downloader_test.go @@ -3,7 +3,7 @@ package download_test import ( "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "strings" @@ -13,6 +13,7 @@ import ( "github.com/go-kit/kit/log" "github.com/Azure/custom-script-extension-linux/pkg/download" + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/ahmetalpbalkan/go-httpbin" "github.com/stretchr/testify/require" ) @@ -29,13 +30,15 @@ func (b *badDownloader) GetRequest() (*http.Request, error) { } func TestDownload_wrapsGetRequestError(t *testing.T) { - _, _, err := download.Download(testctx, new(badDownloader)) + _, _, errCode, err := download.Download(testctx, new(badDownloader)) + require.Equal(t, errCode, errorutil.FileDownload_genericError) require.NotNil(t, err) require.EqualError(t, err, "failed to create http request: expected error") } func TestDownload_wrapsHTTPError(t *testing.T) { - _, _, err := download.Download(testctx, download.NewURLDownload("bad url")) + _, _, errCode, err := download.Download(testctx, download.NewURLDownload("bad url")) + require.Equal(t, errCode, errorutil.FileDownload_unknownError) require.NotNil(t, err) require.Contains(t, err.Error(), "http request failed:") } @@ -53,19 +56,24 @@ func TestDownload_wrapsCommonErrorCodes(t *testing.T) { http.StatusBadRequest, http.StatusUnauthorized, } { - respCode, _, err := download.Download(testctx, download.NewURLDownload(fmt.Sprintf("%s/status/%d", srv.URL, code))) + respCode, _, errCode, err := download.Download(testctx, download.NewURLDownload(fmt.Sprintf("%s/status/%d", srv.URL, code))) require.NotNil(t, err, "not failed for code:%d", code) require.Equal(t, code, respCode) switch respCode { case http.StatusNotFound: + require.Equal(t, errCode, errorutil.FileDownload_doesNotExist) require.Contains(t, err.Error(), "because it does not exist") case http.StatusForbidden: + require.Equal(t, errCode, errorutil.FileDownload_networkingError) require.Contains(t, err.Error(), "Please verify the machine has network connectivity") case http.StatusInternalServerError: + require.Equal(t, errCode, errorutil.Storage_internalServerError) require.Contains(t, err.Error(), "due to an issue with storage") case http.StatusBadRequest: + require.Equal(t, errCode, errorutil.FileDownload_badRequest) require.Contains(t, err.Error(), "because parts of the request were incorrectly formatted, missing, and/or invalid") case http.StatusUnauthorized: + require.Equal(t, errCode, errorutil.FileDownload_accessDenied) require.Contains(t, err.Error(), "because access was denied") } } @@ -75,7 +83,8 @@ func TestDownload_statusOKSucceeds(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - _, body, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/status/200")) + _, body, errCode, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/status/200")) + require.Equal(t, errCode, errorutil.NoError) require.Nil(t, err) defer body.Close() require.NotNil(t, body) @@ -90,13 +99,15 @@ func TestDowload_msiDownloaderErrorMessage(t *testing.T) { msiDownloader404 := download.NewBlobWithMsiDownload(srv.URL+"/status/404", mockMsiProvider) - returnCode, body, err := download.Download(testctx, msiDownloader404) + returnCode, body, errCode, err := download.Download(testctx, msiDownloader404) + require.Equal(t, errCode, errorutil.Msi_notFound) require.True(t, strings.Contains(err.Error(), download.MsiDownload404ErrorString), "error string doesn't contain the correct message") require.Nil(t, body, "body is not nil for failed download") require.Equal(t, 404, returnCode, "return code was not 404") msiDownloader403 := download.NewBlobWithMsiDownload(srv.URL+"/status/403", mockMsiProvider) - returnCode, body, err = download.Download(testctx, msiDownloader403) + returnCode, body, errCode, err = download.Download(testctx, msiDownloader403) + require.Equal(t, errCode, errorutil.Msi_doesNotHaveRightPermissions) require.True(t, strings.Contains(err.Error(), download.MsiDownload403ErrorString), "error string doesn't contain the correct message") require.Nil(t, body, "body is not nil for failed download") require.Equal(t, 403, returnCode, "return code was not 403") @@ -107,10 +118,11 @@ func TestDownload_retrievesBody(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - _, body, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/bytes/65536")) + _, body, errCode, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/bytes/65536")) + require.Equal(t, errCode, errorutil.NoError) require.Nil(t, err) defer body.Close() - b, err := ioutil.ReadAll(body) + b, err := io.ReadAll(body) require.Nil(t, err) require.EqualValues(t, 65536, len(b)) } @@ -119,7 +131,8 @@ func TestDownload_bodyClosesWithoutError(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - _, body, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/get")) + _, body, errCode, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/get")) + require.Equal(t, errCode, errorutil.NoError) require.Nil(t, err) require.Nil(t, body.Close(), "body should close fine") } diff --git a/pkg/download/retry.go b/pkg/download/retry.go index f248064..74f5a35 100644 --- a/pkg/download/retry.go +++ b/pkg/download/retry.go @@ -8,11 +8,9 @@ import ( "os" "time" - "github.com/Azure/azure-extension-platform/vmextension" "github.com/go-kit/kit/log" - github.com/Azure/custom-script-extension-linux/pkg/errorutil - + errorutil "github.com/Azure/custom-script-extension-linux/pkg/errorutil" ) // SleepFunc pauses the execution for at least duration d. @@ -37,17 +35,19 @@ const ( // closed on failures). If the retries do not succeed, the last error is returned. // // It sleeps in exponentially increasing durations between retries. -func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf SleepFunc) (int64, vmextension.ErrorWithClarification) { - var lastErr vmextension.ErrorWithClarification +func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf SleepFunc) (int64, int, error) { + var lastErr error + var lastErrCode int for _, d := range downloaders { for n := 0; n < expRetryN; n++ { ctx := ctx.With("retry", n) // reset the last error before each retry - lastErr = vmextension.NewErrorWithClarification(errorutil.noError, nil) + lastErr = nil + lastErrCode = errorutil.NoError start := time.Now() - status, out, err := Download(ctx, d) - if err.Err == nil { + status, out, errCode, err := Download(ctx, d) + if err == nil { // server returned status code 200 OK // we have a response body, copy it to the file nBytes, innerErr := io.CopyBuffer(f, out, make([]byte, writeBufSize)) @@ -57,7 +57,7 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee out.Close() end := time.Since(start) ctx.Log("info", fmt.Sprintf("file download sucessful: downloaded and saved %d bytes in %d milliseconds", nBytes, end.Milliseconds())) - return nBytes, vmextension.NewErrorWithClarification(errorutil.noError, nil) + return nBytes, lastErrCode, lastErr } else { // we failed to download the response body and write it to file // because either connection was closed prematurely or file write operation failed @@ -66,11 +66,13 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee // clear out the contents of the file so as to not leave a partial file f.Truncate(0) // cache the inner error - lastErr = vmextension.NewErrorWithClarification(errorutil.fileDownload_genericError, innerErr) + lastErrCode = errorutil.FileDownload_genericError + lastErr = innerErr } } else { // cache the outer error lastErr = err + lastErrCode = errCode } // we are here because either server returned a non-200 status code @@ -98,7 +100,7 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee } } } - return 0, lastErr + return 0, lastErrCode, lastErr } func isTransientHttpStatusCode(statusCode int) bool { diff --git a/pkg/download/retry_test.go b/pkg/download/retry_test.go index aeb6b69..605e2d0 100644 --- a/pkg/download/retry_test.go +++ b/pkg/download/retry_test.go @@ -2,7 +2,7 @@ package download_test import ( "fmt" - "io/ioutil" + // "io/ioutil" "net/http" "net/http/httptest" "os" @@ -168,8 +168,8 @@ func TestRetriesWith_LargeFileThatTimesOutWhileDownloading(t *testing.T) { largeFileDownloader := mockDownloader{0, srv.URL + "/bytes/" + fmt.Sprintf("%d", size)} sr := new(sleepRecorder) - n, err := download.WithRetries(nopLog(), file, []download.Downloader{&largeFileDownloader}, sr.Sleep) - require.NotNil(t, err, "download with retries should fail because of server timeout") + n, ewc := download.WithRetries(nopLog(), file, []download.Downloader{&largeFileDownloader}, sr.Sleep) + require.NotNil(t, ewc.Err, "download with retries should fail because of server timeout") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") fi, err := file.Stat() @@ -178,8 +178,8 @@ func TestRetriesWith_LargeFileThatTimesOutWhileDownloading(t *testing.T) { } func CreateTestFile(t *testing.T) (string, *os.File) { - dir, err := ioutil.TempDir("", "") - require.Nil(t, err) + dir := os.TempDir() + // require.Nil(t, err) path := filepath.Join(dir, "test-file") diff --git a/pkg/download/save.go b/pkg/download/save.go index f45c46d..a7cc06c 100644 --- a/pkg/download/save.go +++ b/pkg/download/save.go @@ -3,27 +3,26 @@ package download import ( "os" - "github.com/Azure/azure-extension-platform/vmextension" "github.com/go-kit/kit/log" "github.com/pkg/errors" - github.com/Azure/custom-script-extension-linux/pkg/errorutil + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" ) // SaveTo uses given downloader to fetch the resource with retries and saves the // given file. Directory of dst is not created by this function. If a file at // dst exists, it will be truncated. If a new file is created, mode is used to // set the permission bits. Written number of bytes are returned on success. -func SaveTo(ctx *log.Context, d []Downloader, dst string, mode os.FileMode) (int64, vmextension.ErrorWithClarification) { +func SaveTo(ctx *log.Context, d []Downloader, dst string, mode os.FileMode) (int64, int, error) { f, err := os.OpenFile(dst, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, mode) if err != nil { - return 0, vmextension.NewErrorWithClarification(errorutil.unknownError, errors.Wrap(err, "failed to open file for writing")) + return 0, errorutil.FileDownload_unknownError, errors.Wrap(err, "failed to open file for writing") } defer f.Close() - n, ewc := WithRetries(ctx, f, d, ActualSleep) - if ewc.Err != nil { - return n, vmextension.NewErrorWithClarification(ewc.ErrorCode, errors.Wrapf(ewc.Err, "failed to download response and write to file: %s", dst)) + n, errCode, err := WithRetries(ctx, f, d, ActualSleep) + if err != nil { + return n, errCode, errors.Wrapf(err, "failed to download response and write to file: %s", dst) } - return n, vmextension.NewErrorWithClarification(errorutil.noError, nil) + return n, errorutil.NoError, nil } diff --git a/pkg/errorutil/errorclarificationcodes.go b/pkg/errorutil/errorclarificationcodes.go index a501c1b..11341d3 100644 --- a/pkg/errorutil/errorclarificationcodes.go +++ b/pkg/errorutil/errorclarificationcodes.go @@ -6,46 +6,47 @@ import ( const ( // System errors - fileDownload_badRequest int = -41 - fileDownload_unknownError int = -40 + FileDownload_badRequest int = -41 + FileDownload_unknownError int = -40 - imds_internalMsiError int = -30 + Imds_internalMsiError int = -30 - internal_badConfig int = -21 - internal_couldNotFindCertificate int = -20 + Internal_badConfig int = -21 + Internal_couldNotFindCertificate int = -20 - storage_internalServerError int = -1 - systemError int = 0 // CRP interprets anything > 0 as user errors + Storage_internalServerError int = -1 + SystemError int = 0 // CRP interprets anything > 0 as user errors // User errors - commandExecution_failedUnknownError int = 1 - commandExecution_failureExitCode int = 2 - commandExecution_interruptedByVmShutdown int = 3 - - customerInput_commandToExecuteSpecifiedInTwoPlaces int = 20 - customerInput_fileUrisSpecifiedInTwoPlaces int = 22 - customerInput_commandToExecuteAndScriptNotSpecified int = 23 - customerInput_fileUriContainsNull int = 24 - customerInput_invalidFileUris int = 25 - customerInput_storageCredsAndMIBothSpecified int = 26 - customerInput_clientIdObjectIdBothSpecified int = 27 - customerInput_scriptSpecifiedInTwoPlaces int = 28 - customerInput_commandToExecuteAndScriptBothSpecified int = 29 - customerInput_incompleteStorageCreds int = 30 - - fileDownload_unableToCreateDownloadDirectory int = 50 - fileDownload_sasExpired int = 51 - fileDownload_accessDenied int = 52 - fileDownload_doesNotExist int = 53 - fileDownload_networkingError int = 54 - fileDownload_genericError int = 55 - fileDownload_exceededTimeout int = 56 - - msi_notFound int = 70 - msi_doesNotHaveRightPermissions int = 71 - msi_GenericRetrievalError int = 72 + CommandExecution_failedUnknownError int = 1 + CommandExecution_failureExitCode int = 2 + CommandExecution_interruptedByVmShutdown int = 3 + + CustomerInput_commandToExecuteSpecifiedInTwoPlaces int = 20 + CustomerInput_fileUrisSpecifiedInTwoPlaces int = 22 + CustomerInput_commandToExecuteAndScriptNotSpecified int = 23 + CustomerInput_fileUriContainsNull int = 24 + CustomerInput_invalidFileUris int = 25 + CustomerInput_storageCredsAndMIBothSpecified int = 26 + CustomerInput_clientIdObjectIdBothSpecified int = 27 + CustomerInput_scriptSpecifiedInTwoPlaces int = 28 + CustomerInput_commandToExecuteAndScriptBothSpecified int = 29 + CustomerInput_incompleteStorageCreds int = 30 + + FileDownload_unableToCreateDownloadDirectory int = 50 + FileDownload_sasExpired int = 51 + FileDownload_accessDenied int = 52 + FileDownload_doesNotExist int = 53 + FileDownload_networkingError int = 54 + FileDownload_genericError int = 55 + FileDownload_exceededTimeout int = 56 + + Msi_notFound int = 70 + Msi_doesNotHaveRightPermissions int = 71 + Msi_GenericRetrievalError int = 72 // No Error - used as a placeholder value // when representing an "empty" ErrorWithClarification - noError int = math.MaxInt + // or when the error can be treated without the clarification + NoError int = math.MaxInt ) From 11302c25ae973fe1c8861f6cf0453bb1d66bf80d Mon Sep 17 00:00:00 2001 From: Deepti Vaidyanathan Date: Wed, 12 Feb 2025 23:51:28 +0000 Subject: [PATCH 4/4] Fixes tests --- go.mod | 40 +++------- go.sum | 111 ++++---------------------- main/cmds.go | 2 +- main/cmds_test.go | 14 ++-- main/exec.go | 4 +- main/exec_test.go | 35 ++++---- main/files_test.go | 16 ++-- main/handlersettings_test.go | 32 ++++---- main/status.go | 22 ++--- main/status_test.go | 20 +++++ pkg/download/blobwithmsitoken_test.go | 4 +- pkg/download/downloader.go | 19 +++-- pkg/download/downloader_test.go | 84 +++++++++++-------- pkg/download/retry.go | 16 ++-- pkg/download/retry_test.go | 16 ++-- pkg/download/save.go | 15 ++-- pkg/download/save_test.go | 16 ++-- 17 files changed, 216 insertions(+), 250 deletions(-) diff --git a/go.mod b/go.mod index 4836129..9340f73 100644 --- a/go.mod +++ b/go.mod @@ -1,48 +1,34 @@ module github.com/Azure/custom-script-extension-linux -go 1.23.4 +go 1.23 + +toolchain go1.23.5 require ( - github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6 - github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb - github.com/go-kit/kit v0.13.0 + github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 + github.com/Azure/azure-extension-platform v0.0.0-20250107200156-aa20f765d49f + github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible + github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 + github.com/go-kit/kit v0.12.0 + github.com/google/uuid v1.3.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 github.com/xeipuuv/gojsonschema v1.2.0 + golang.org/x/text v0.9.0 ) require ( - github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 // indirect - github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.29 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/andybalholm/brotli v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-kit/log v0.2.1 github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 // indirect + github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sys v0.5.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -// //DELETE after testing -//require github.com/Azure/custom-script-extension-linux/pkg v0.0.0 - -//replace github.com/Azure/custom-script-extension-linux/pkg => ../custom-script-extension-linux/pkg - replace github.com/go-kit/kit => github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 diff --git a/go.sum b/go.sum index ffaaa0a..2800b39 100644 --- a/go.sum +++ b/go.sum @@ -1,56 +1,28 @@ github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187 h1:C4S32XsUvctWzdWDEYlvhfcgH1iGvSD62II7Dd7F6B8= github.com/Azure/azure-extension-foundation v0.0.0-20230404211847-9858bdd5c187/go.mod h1:a0BFq9UoWBHvBS7iagvjFqBjYfxtBsmqvCLWIHRq9b0= -github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6 h1:RDJpiDBkLFa711zwULKd1bhIoWpaslIwlfz5CBHn+eI= -github.com/Azure/azure-extension-platform v0.0.0-20241219234143-33858f5985a6/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4= -github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb h1:DSAtit+Eq3K2/kzZsSBYSh3jfCBiWm+FMJkFF0Ffy+I= -github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb/go.mod h1:Rg55S63lgqSBCawY/oTm7jdFSySp6jwIqgHMB2IeHK8= -github.com/ahmetb/go-httpbin v0.0.0-20240315150752-da45896c98cb h1:od9/PvyZ6X+dCU04fTRyrYS8HKVW1SxprFvLYjzUI0U= -github.com/ahmetb/go-httpbin v0.0.0-20240315150752-da45896c98cb/go.mod h1:iB3NbHoh0P/9AZepPBcH+gM1PhQJGmsres+ZHf72M3k= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954 h1:Jnedpc6riCirNEUQGZ7I7gH1PZym71sKFnbP9Lb32oA= +github.com/Azure/azure-extension-platform v0.0.0-20230218002700-ca684482c954/go.mod h1:1hEkO8M1zN/SQpdFOTDDMTNfeE1Q2tCHmEXXiHrWTgo= +github.com/Azure/azure-extension-platform v0.0.0-20250107200156-aa20f765d49f h1:ddsUz/suc9txCMz/xWOslqNMvzhbWFMTflUrbcMNoSw= +github.com/Azure/azure-extension-platform v0.0.0-20250107200156-aa20f765d49f/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY= +github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible h1:7CctKV2SGUVFq3a+WNHypGRKQzaPCNVEAMMAlEJXrWc= +github.com/Azure/azure-sdk-for-go v3.1.0-beta.0.20160802173609-87de771fcdf5+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1 h1:/sPElPBMLSi6+bV0o0fPN4U24qQCNHs1i/BjnO+GqLc= +github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1/go.mod h1:Rg55S63lgqSBCawY/oTm7jdFSySp6jwIqgHMB2IeHK8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2 h1:awXynDTA1TiAp1SA/o/xoU6oRHE3xKCokck9l4/poMc= github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 h1:3nOfQt8sRPYbXORD5tJ8YyQ3HlL2Jt3LJ2U17CbNh6I= +github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b h1:OFvZV3a+25cGJH9dETHw0nk0wV6hLZI7IJijOkXEFS0= +github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -75,64 +47,13 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main/cmds.go b/main/cmds.go index d118505..581a377 100644 --- a/main/cmds.go +++ b/main/cmds.go @@ -253,7 +253,7 @@ func runCmd(ctx log.Logger, dir string, cfg handlerSettings) (ewc vmextension.Er if ewc.Err != nil { ctx.Log("event", "failed to execute command", "error", err, "output", dir) - return vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrap(err, "failed to execute command")) + return vmextension.NewErrorWithClarification(ewc.ErrorCode, errors.Wrap(ewc.Err, "failed to execute command")) } ctx.Log("event", "executed command", "output", dir) return vmextension.NewErrorWithClarification(errorutil.NoError, nil) diff --git a/main/cmds_test.go b/main/cmds_test.go index d6294df..14c7920 100644 --- a/main/cmds_test.go +++ b/main/cmds_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "testing" + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/ahmetalpbalkan/go-httpbin" "github.com/go-kit/kit/log" "github.com/stretchr/testify/require" @@ -84,7 +85,7 @@ func Test_runCmd_success(t *testing.T) { require.Nil(t, runCmd(log.NewNopLogger(), dir, handlerSettings{ publicSettings: publicSettings{CommandToExecute: "date"}, - }), "command should run successfully") + }).Err, "command should run successfully") // check stdout stderr files _, err = os.Stat(filepath.Join(dir, "stdout")) @@ -98,11 +99,12 @@ func Test_runCmd_fail(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) - err = runCmd(log.NewNopLogger(), dir, handlerSettings{ + ewc := runCmd(log.NewNopLogger(), dir, handlerSettings{ publicSettings: publicSettings{CommandToExecute: "non-existing-cmd"}, }) - require.NotNil(t, err, "command terminated with exit status") - require.Contains(t, err.Error(), "failed to execute command") + require.Equal(t, errorutil.CommandExecution_failureExitCode, ewc.ErrorCode) + require.NotNil(t, ewc.Err, "command terminated with exit status") + require.Contains(t, ewc.Err.Error(), "failed to execute command") } func Test_downloadFiles(t *testing.T) { @@ -113,7 +115,7 @@ func Test_downloadFiles(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - err = downloadFiles(log.NewContext(log.NewNopLogger()), + ewc := downloadFiles(log.NewContext(log.NewNopLogger()), dir, handlerSettings{ publicSettings: publicSettings{ @@ -123,7 +125,7 @@ func Test_downloadFiles(t *testing.T) { srv.URL + "/bytes/1000", }}, }) - require.Nil(t, err) + require.Nil(t, ewc.Err) // check the files f := []string{"10", "100", "1000"} diff --git a/main/exec.go b/main/exec.go index 9b97375..96ed557 100644 --- a/main/exec.go +++ b/main/exec.go @@ -32,13 +32,13 @@ func Exec(cmd, workdir string, stdout, stderr io.WriteCloser) (int, vmextension. if ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { code := status.ExitStatus() - return code, vmextension.NewErrorWithClarification(errorutil.CommandExecution_failedUnknownError, fmt.Errorf("command terminated with exit status=%d", code)) + return code, vmextension.NewErrorWithClarification(errorutil.CommandExecution_failureExitCode, fmt.Errorf("command terminated with exit status=%d", code)) } } if err == nil { return 0, vmextension.NewErrorWithClarification(errorutil.NoError, nil) } - return 0, vmextension.NewErrorWithClarification(errorutil.NoError, errors.Wrapf(err, "failed to execute command")) + return 0, vmextension.NewErrorWithClarification(errorutil.CommandExecution_failedUnknownError, errors.Wrapf(err, "failed to execute command")) } diff --git a/main/exec_test.go b/main/exec_test.go index e01b34c..cb98e5b 100644 --- a/main/exec_test.go +++ b/main/exec_test.go @@ -8,13 +8,14 @@ import ( "path/filepath" "testing" + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/stretchr/testify/require" ) func TestExec_success(t *testing.T) { v := new(mockFile) ec, err := Exec("date", "/", v, v) - require.Nil(t, err, "err: %v -- out: %s", err, v.b.Bytes()) + require.Nil(t, err.Err, "err: %v -- out: %s", err.Err, v.b.Bytes()) require.EqualValues(t, 0, ec) } @@ -24,7 +25,7 @@ func TestExec_success_redirectsStdStreams_closesFds(t *testing.T) { require.False(t, e.closed, "stderr open") _, err := Exec("/bin/echo 'I am stdout!'>&1; /bin/echo 'I am stderr!'>&2", "/", o, e) - require.Nil(t, err, "err: %v -- stderr: %s", err, e.b.Bytes()) + require.Nil(t, err.Err, "err: %v -- stderr: %s", err.Err, e.b.Bytes()) require.Equal(t, "I am stdout!\n", string(o.b.Bytes())) require.Equal(t, "I am stderr!\n", string(e.b.Bytes())) require.True(t, o.closed, "stdout closed") @@ -33,15 +34,17 @@ func TestExec_success_redirectsStdStreams_closesFds(t *testing.T) { func TestExec_failure_exitError(t *testing.T) { ec, err := Exec("exit 12", "/", new(mockFile), new(mockFile)) - require.NotNil(t, err) - require.EqualError(t, err, "command terminated with exit status=12") // error is customized + require.Equal(t, err.ErrorCode, errorutil.CommandExecution_failureExitCode) + require.NotNil(t, err.Err) + require.EqualError(t, err.Err, "command terminated with exit status=12") // error is customized require.EqualValues(t, 12, ec) } func TestExec_failure_genericError(t *testing.T) { _, err := Exec("date", "/non-existing-path", new(mockFile), new(mockFile)) - require.NotNil(t, err) - require.Contains(t, err.Error(), "failed to execute command:") // error is wrapped + require.Equal(t, err.ErrorCode, errorutil.CommandExecution_failedUnknownError) + require.NotNil(t, err.Err) + require.Contains(t, err.Err.Error(), "failed to execute command:") // error is wrapped } func TestExec_failure_fdClosed(t *testing.T) { @@ -49,8 +52,9 @@ func TestExec_failure_fdClosed(t *testing.T) { require.Nil(t, out.Close()) _, err := Exec("date", "/", out, out) - require.NotNil(t, err) - require.Contains(t, err.Error(), "file closed") // error is wrapped + require.Equal(t, err.ErrorCode, errorutil.CommandExecution_failedUnknownError) + require.NotNil(t, err.Err) + require.Contains(t, err.Err.Error(), "file closed") // error is wrapped } func TestExec_failure_redirectsStdStreams_closesFds(t *testing.T) { @@ -59,7 +63,8 @@ func TestExec_failure_redirectsStdStreams_closesFds(t *testing.T) { require.False(t, e.closed, "stderr open") _, err := Exec(`/bin/echo 'I am stdout!'>&1; /bin/echo 'I am stderr!'>&2; exit 12`, "/", o, e) - require.NotNil(t, err) + require.Equal(t, err.ErrorCode, errorutil.CommandExecution_failureExitCode) + require.NotNil(t, err.Err) require.Equal(t, "I am stdout!\n", string(o.b.Bytes())) require.Equal(t, "I am stderr!\n", string(e.b.Bytes())) require.True(t, o.closed, "stdout closed") @@ -71,8 +76,8 @@ func TestExecCmdInDir(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) - err = ExecCmdInDir("/bin/echo 'Hello world'", dir) - require.Nil(t, err) + ewc := ExecCmdInDir("/bin/echo 'Hello world'", dir) + require.Nil(t, ewc.Err) require.True(t, fileExists(t, filepath.Join(dir, "stdout")), "stdout file should be created") require.True(t, fileExists(t, filepath.Join(dir, "stderr")), "stderr file should be created") @@ -87,7 +92,9 @@ func TestExecCmdInDir(t *testing.T) { func TestExecCmdInDir_cantOpenError(t *testing.T) { err := ExecCmdInDir("/bin/echo 'Hello world'", "/non-existing-dir") - require.Contains(t, err.Error(), "failed to open stdout file") + require.Equal(t, err.ErrorCode, errorutil.NoError) + require.NotNil(t, err.Err) + require.Contains(t, err.Err.Error(), "failed to open stdout file") } func TestExecCmdInDir_truncates(t *testing.T) { @@ -95,8 +102,8 @@ func TestExecCmdInDir_truncates(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) - require.Nil(t, ExecCmdInDir("/bin/echo '1:out'; /bin/echo '1:err'>&2", dir)) - require.Nil(t, ExecCmdInDir("/bin/echo '2:out'; /bin/echo '2:err'>&2", dir)) + require.Nil(t, ExecCmdInDir("/bin/echo '1:out'; /bin/echo '1:err'>&2", dir).Err) + require.Nil(t, ExecCmdInDir("/bin/echo '2:out'; /bin/echo '2:err'>&2", dir).Err) b, err := ioutil.ReadFile(filepath.Join(dir, "stdout")) require.Nil(t, err) diff --git a/main/files_test.go b/main/files_test.go index 72e60a6..0abc412 100644 --- a/main/files_test.go +++ b/main/files_test.go @@ -16,11 +16,11 @@ import ( func Test_getDownloader_azureBlob(t *testing.T) { // error condition _, err := getDownloaders("http://acct.blob.core.windows.net/", "acct", "key", nil) - require.NotNil(t, err) + require.NotNil(t, err.Err) // valid input d, err := getDownloaders("http://acct.blob.core.windows.net/container/blob", "acct", "key", nil) - require.Nil(t, err) + require.Nil(t, err.Err) require.NotNil(t, d) require.Equal(t, 1, len(d)) require.Equal(t, "download.blobDownload", fmt.Sprintf("%T", d[0]), "got wrong type") @@ -28,14 +28,14 @@ func Test_getDownloader_azureBlob(t *testing.T) { func Test_getDownloader_externalUrl(t *testing.T) { d, err := getDownloaders("http://acct.blob.core.windows.net/", "", "", nil) - require.Nil(t, err) + require.Nil(t, err.Err) require.NotNil(t, d) require.NotEmpty(t, d) require.Equal(t, 1, len(d)) require.Equal(t, "download.urlDownload", fmt.Sprintf("%T", d[0]), "got wrong type") d, err = getDownloaders("http://acct.blob.core.windows.net/", "", "", &clientOrObjectId{"", "dummyclientid"}) - require.Nil(t, err) + require.Nil(t, err.Err) require.NotNil(t, d) require.NotEmpty(t, d) require.Equal(t, 2, len(d)) @@ -43,13 +43,13 @@ func Test_getDownloader_externalUrl(t *testing.T) { require.Equal(t, "*download.blobWithMsiToken", fmt.Sprintf("%T", d[1]), "got wrong type") d, err = getDownloaders("http://acct.blob.core.windows.net/", "foo", "", nil) - require.Nil(t, err) + require.Nil(t, err.Err) require.NotNil(t, d) require.Equal(t, 1, len(d)) require.Equal(t, "download.urlDownload", fmt.Sprintf("%T", d[0]), "got wrong type") d, err = getDownloaders("http://acct.blob.core.windows.net/", "", "bar", nil) - require.Nil(t, err) + require.Nil(t, err.Err) require.NotNil(t, d) require.Equal(t, 1, len(d)) require.Equal(t, "download.urlDownload", fmt.Sprintf("%T", d[0]), "got wrong type") @@ -124,8 +124,8 @@ func Test_downloadAndProcessURL(t *testing.T) { defer os.RemoveAll(tmpDir) cfg := handlerSettings{publicSettings{}, protectedSettings{StorageAccountName: "", StorageAccountKey: ""}} - err = downloadAndProcessURL(log.NewContext(log.NewNopLogger()), srv.URL+"/bytes/256", tmpDir, &cfg) - require.Nil(t, err) + ewc := downloadAndProcessURL(log.NewContext(log.NewNopLogger()), srv.URL+"/bytes/256", tmpDir, &cfg) + require.Nil(t, ewc.Err) fp := filepath.Join(tmpDir, "256") fi, err := os.Stat(fp) diff --git a/main/handlersettings_test.go b/main/handlersettings_test.go index 3addd72..7097aa0 100644 --- a/main/handlersettings_test.go +++ b/main/handlersettings_test.go @@ -11,30 +11,30 @@ func Test_handlerSettingsValidate(t *testing.T) { require.Equal(t, errCmdMissing, handlerSettings{ publicSettings{}, protectedSettings{}, - }.validate()) + }.validate().Err) // commandToExecute specified twice require.Equal(t, errCmdTooMany, handlerSettings{ publicSettings{CommandToExecute: "foo"}, protectedSettings{CommandToExecute: "foo"}, - }.validate()) + }.validate().Err) // script specified twice require.Equal(t, errScriptTooMany, handlerSettings{ publicSettings{Script: "foo"}, protectedSettings{Script: "foo"}, - }.validate()) + }.validate().Err) // commandToExecute and script both specified require.Equal(t, errCmdAndScript, handlerSettings{ publicSettings{CommandToExecute: "foo"}, protectedSettings{Script: "foo"}, - }.validate()) + }.validate().Err) require.Equal(t, errCmdAndScript, handlerSettings{ publicSettings{Script: "foo"}, protectedSettings{CommandToExecute: "foo"}, - }.validate()) + }.validate().Err) // storageAccount name specified; but not key require.Equal(t, errStoragePartialCredentials, handlerSettings{ @@ -42,7 +42,7 @@ func Test_handlerSettingsValidate(t *testing.T) { CommandToExecute: "date", StorageAccountName: "foo", StorageAccountKey: ""}, - }.validate()) + }.validate().Err) // storageAccount key specified; but not name require.Equal(t, errStoragePartialCredentials, handlerSettings{ @@ -50,7 +50,7 @@ func Test_handlerSettingsValidate(t *testing.T) { CommandToExecute: "date", StorageAccountName: "", StorageAccountKey: "foo"}, - }.validate()) + }.validate().Err) } func Test_commandToExecutePrivateIfNotPublic(t *testing.T) { @@ -96,14 +96,14 @@ func Test_managedIdentityVerification(t *testing.T) { ManagedIdentity: &clientOrObjectId{ ClientId: "31b403aa-c364-4240-a7ff-d85fb6cd7232", }, - }}.validate(), "validation failed for settings with MSI") + }}.validate().Err, "validation failed for settings with MSI") require.NoError(t, handlerSettings{publicSettings{}, protectedSettings{ CommandToExecute: "echo hi", ManagedIdentity: &clientOrObjectId{ ObjectId: "31b403aa-c364-4240-a7ff-d85fb6cd7232", }, - }}.validate(), "validation failed for settings with MSI") + }}.validate().Err, "validation failed for settings with MSI") require.Equal(t, errUsingBothKeyAndMsi, handlerSettings{publicSettings{}, @@ -114,7 +114,7 @@ func Test_managedIdentityVerification(t *testing.T) { ManagedIdentity: &clientOrObjectId{ ObjectId: "31b403aa-c364-4240-a7ff-d85fb6cd7232", }, - }}.validate(), "validation didn't fail for settings with both MSI and storage account") + }}.validate().Err, "validation didn't fail for settings with both MSI and storage account") require.Equal(t, errUsingBothClientIdAndObjectId, handlerSettings{publicSettings{}, @@ -124,7 +124,7 @@ func Test_managedIdentityVerification(t *testing.T) { ObjectId: "31b403aa-c364-4240-a7ff-d85fb6cd7232", ClientId: "31b403aa-c364-4240-a7ff-d85fb6cd7232", }, - }}.validate(), "validation didn't fail for settings with both MSI and storage account") + }}.validate().Err, "validation didn't fail for settings with both MSI and storage account") } func Test_toJSON_empty(t *testing.T) { @@ -148,7 +148,7 @@ func Test_toJSONUmarshallForManagedIdentity(t *testing.T) { require.NoError(t, err, "error while deserializing json") require.Nil(t, protSettings.ManagedIdentity, "ProtectedSettings.ManagedIdentity was expected to be nil") h := handlerSettings{publicSettings{}, *protSettings} - require.NoError(t, h.validate(), "settings should be valid") + require.NoError(t, h.validate().Err, "settings should be valid") testString = `{"commandToExecute" : "echo hello", "fileUris":["https://a.com/file.txt"], "managedIdentity": { }}` require.NoError(t, validateProtectedSettings(testString), "protected settings should be valid") @@ -159,7 +159,7 @@ func Test_toJSONUmarshallForManagedIdentity(t *testing.T) { require.Equal(t, protSettings.ManagedIdentity.ClientId, "") require.Equal(t, protSettings.ManagedIdentity.ObjectId, "") h = handlerSettings{publicSettings{}, *protSettings} - require.NoError(t, h.validate(), "settings should be valid") + require.NoError(t, h.validate().Err, "settings should be valid") testString = `{"commandToExecute" : "echo hello", "fileUris":["https://a.com/file.txt", "https://b.com/file2.txt"], "managedIdentity": { "clientId": "31b403aa-c364-4240-a7ff-d85fb6cd7232"}}` require.NoError(t, validateProtectedSettings(testString), "protected settings should be valid") @@ -170,7 +170,7 @@ func Test_toJSONUmarshallForManagedIdentity(t *testing.T) { require.Equal(t, protSettings.ManagedIdentity.ClientId, "31b403aa-c364-4240-a7ff-d85fb6cd7232") require.Equal(t, protSettings.ManagedIdentity.ObjectId, "") h = handlerSettings{publicSettings{}, *protSettings} - require.NoError(t, h.validate(), "settings should be valid") + require.NoError(t, h.validate().Err, "settings should be valid") testString = `{"commandToExecute" : "echo hello", "fileUris":["https://a.com/file.txt"], "managedIdentity": { "objectId": "31b403aa-c364-4240-a7ff-d85fb6cd7232"}}` require.NoError(t, validateProtectedSettings(testString), "protected settings should be valid") @@ -181,7 +181,7 @@ func Test_toJSONUmarshallForManagedIdentity(t *testing.T) { require.Equal(t, protSettings.ManagedIdentity.ObjectId, "31b403aa-c364-4240-a7ff-d85fb6cd7232") require.Equal(t, protSettings.ManagedIdentity.ClientId, "") h = handlerSettings{publicSettings{}, *protSettings} - require.NoError(t, h.validate(), "settings should be valid") + require.NoError(t, h.validate().Err, "settings should be valid") testString = `{"commandToExecute" : "echo hello", "fileUris":["https://a.com/file.txt", "https://b.com/file2.txt"], "managedIdentity": { "clientId": "31b403aa-c364-4240-a7ff-d85fb6cd7232", "objectId": "41b403aa-c364-4240-a7ff-d85fb6cd7232"}}` require.NoError(t, validateProtectedSettings(testString), "protected settings should be valid") @@ -192,5 +192,5 @@ func Test_toJSONUmarshallForManagedIdentity(t *testing.T) { require.Equal(t, protSettings.ManagedIdentity.ClientId, "31b403aa-c364-4240-a7ff-d85fb6cd7232") require.Equal(t, protSettings.ManagedIdentity.ObjectId, "41b403aa-c364-4240-a7ff-d85fb6cd7232") h = handlerSettings{publicSettings{}, *protSettings} - require.Error(t, h.validate(), "settings should be invalid") + require.Error(t, h.validate().Err, "settings should be invalid") } diff --git a/main/status.go b/main/status.go index 5505598..0395e08 100644 --- a/main/status.go +++ b/main/status.go @@ -10,6 +10,7 @@ import ( status "github.com/Azure/azure-extension-platform/pkg/status" vmextension "github.com/Azure/azure-extension-platform/vmextension" + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/go-kit/kit/log" "github.com/pkg/errors" ) @@ -104,22 +105,25 @@ func reportStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t Type, return nil } -// reportStatus saves operation status to the status file for the extension -// handler with the optional given message, if the given cmd requires reporting -// status. +// reportErrorStatus saves the error(s) that occurred during the operation +// to the status file for the extension handler with clarification messages and codes, +// if the given cmd requires reporting status. // // If an error occurs reporting the status, it will be logged and returned. -func reportErrorStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t Type, c cmd, err vmextension.ErrorWithClarification) error { +func reportErrorStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t Type, c cmd, ewc vmextension.ErrorWithClarification) error { if !c.shouldReportStatus { ctx.Log("status", "not reported for operation (by design)") return nil } - ewc := status.ErrorClarification{ - Code: err.ErrorCode, - Message: err.Error(), + var err error + if ewc.ErrorCode == errorutil.NoError { + s := NewStatus(t, c.name, statusMsg(c, t, ewc.Err.Error())) + err = s.Save(hEnv.HandlerEnvironment.StatusFolder, seqNum) + } else { + s := status.NewError(c.name, status.ErrorClarification{Code: ewc.ErrorCode, Message: ewc.Error()}) + err = s.Save(hEnv.HandlerEnvironment.StatusFolder, uint(seqNum)) } - s := status.NewError(c.name, ewc) - if err := s.Save(hEnv.HandlerEnvironment.StatusFolder, uint(seqNum)); err != nil { + if err != nil { ctx.Log("event", "failed to save handler status", "error", err) return errors.Wrap(err, "failed to save handler status") } diff --git a/main/status_test.go b/main/status_test.go index 770180d..c13c921 100644 --- a/main/status_test.go +++ b/main/status_test.go @@ -1,11 +1,14 @@ package main import ( + "fmt" "io/ioutil" "os" "path/filepath" "testing" + vmextension "github.com/Azure/azure-extension-platform/vmextension" + "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/go-kit/kit/log" "github.com/stretchr/testify/require" ) @@ -46,6 +49,23 @@ func Test_reportStatus_fileExists(t *testing.T) { require.NotEqual(t, 0, len(b), ".status file not empty") } +func Test_reportErrorStatus_fileExists(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "") + require.Nil(t, err) + defer os.RemoveAll(tmpDir) + + fakeEnv := HandlerEnvironment{} + fakeEnv.HandlerEnvironment.StatusFolder = tmpDir + ewc := vmextension.NewErrorWithClarification(errorutil.CommandExecution_failureExitCode, fmt.Errorf("command failed with exit code = 1")) + + require.Nil(t, reportErrorStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, StatusError, cmdEnable, ewc)) + + path := filepath.Join(tmpDir, "1.status") + b, err := ioutil.ReadFile(path) + require.Nil(t, err, ".status file exists") + require.NotEqual(t, 0, len(b), ".status file not empty") +} + func Test_reportStatus_checksIfShouldBeReported(t *testing.T) { for _, c := range cmds { tmpDir, err := ioutil.TempDir("", "status-"+c.name) diff --git a/pkg/download/blobwithmsitoken_test.go b/pkg/download/blobwithmsitoken_test.go index ec33db0..7134cda 100644 --- a/pkg/download/blobwithmsitoken_test.go +++ b/pkg/download/blobwithmsitoken_test.go @@ -46,8 +46,8 @@ func Test_realDownloadBlobWithMsiToken(t *testing.T) { err := json.Unmarshal([]byte(msiJson), &msi) return msi, err }} - _, stream, err := Download(testctx, &downloader) - require.NoError(t, err, "File download failed") + _, stream, ewc := Download(testctx, &downloader) + require.NoError(t, ewc.Err, "File download failed") defer stream.Close() bytes, err := ioutil.ReadAll(stream) diff --git a/pkg/download/downloader.go b/pkg/download/downloader.go index cb29f01..92228f9 100644 --- a/pkg/download/downloader.go +++ b/pkg/download/downloader.go @@ -8,6 +8,7 @@ import ( "time" "net/url" + "github.com/Azure/azure-extension-platform/vmextension" "github.com/Azure/custom-script-extension-linux/pkg/errorutil" "github.com/Azure/custom-script-extension-linux/pkg/urlutil" "github.com/go-kit/kit/log" @@ -24,6 +25,8 @@ type Downloader interface { const ( MsiDownload404ErrorString = "please ensure that the blob location in the fileUri setting exists, and the specified Managed Identity has read permissions to the storage blob" MsiDownload403ErrorString = "please ensure that the specified Managed Identity has read permissions to the storage blob" + MsiDownloadGenericErrorString = "unable to download the MSI. This may be due to firewall rules or a networking error" + MsiDownload500ErrorString = "the IMDS service returned a00 upon requesting the MSI" ) var ( @@ -46,10 +49,10 @@ var ( // Download retrieves a response body and checks the response status code to see // if it is 200 OK and then returns the response body. It issues a new request // every time called. It is caller's responsibility to close the response body. -func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, int, error) { +func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, vmextension.ErrorWithClarification) { req, err := d.GetRequest() if err != nil { - return -1, nil, errorutil.FileDownload_genericError, errors.Wrapf(err, "failed to create http request") + return -1, nil, vmextension.NewErrorWithClarification(errorutil.FileDownload_genericError, errors.Wrapf(err, "failed to create http request")) } requestID := req.Header.Get(xMsClientRequestIdHeaderName) if len(requestID) > 0 { @@ -59,15 +62,15 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, int, error) { if err != nil { if ((err.(*url.Error)).Timeout()) { err = urlutil.RemoveUrlFromErr(err) - return -1, nil, errorutil.FileDownload_exceededTimeout, errors.Wrapf(err, "http request timed out") + return -1, nil, vmextension.NewErrorWithClarification(errorutil.FileDownload_exceededTimeout, errors.Wrapf(err, "http request timed out")) } err = urlutil.RemoveUrlFromErr(err) - return -1, nil, errorutil.FileDownload_unknownError, errors.Wrapf(err, "http request failed") + return -1, nil, vmextension.NewErrorWithClarification(errorutil.FileDownload_unknownError, errors.Wrapf(err, "http request failed")) } if resp.StatusCode == http.StatusOK { // We're setting the errorCode to MaxInt because we're only checking whether the internal error is nil - return resp.StatusCode, resp.Body, errorutil.NoError, nil + return resp.StatusCode, resp.Body, vmextension.NewErrorWithClarification(errorutil.NoError, nil) } errString := "" @@ -82,7 +85,11 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, int, error) { case http.StatusForbidden: errString = MsiDownload403ErrorString errClarificationCode = errorutil.Msi_doesNotHaveRightPermissions + case http.StatusInternalServerError: + errString = MsiDownload500ErrorString + errClarificationCode = errorutil.Imds_internalMsiError default: + errString = MsiDownloadGenericErrorString errClarificationCode = errorutil.Msi_GenericRetrievalError } break @@ -121,5 +128,5 @@ func Download(ctx *log.Context, d Downloader) (int, io.ReadCloser, int, error) { if len(requestId) > 0 { errString += fmt.Sprintf(" (Service request ID: %s)", requestId) } - return resp.StatusCode, nil, errClarificationCode, fmt.Errorf(errString) + return resp.StatusCode, nil, vmextension.NewErrorWithClarification(errClarificationCode, fmt.Errorf(errString)) } diff --git a/pkg/download/downloader_test.go b/pkg/download/downloader_test.go index 7657154..f784000 100644 --- a/pkg/download/downloader_test.go +++ b/pkg/download/downloader_test.go @@ -30,17 +30,17 @@ func (b *badDownloader) GetRequest() (*http.Request, error) { } func TestDownload_wrapsGetRequestError(t *testing.T) { - _, _, errCode, err := download.Download(testctx, new(badDownloader)) - require.Equal(t, errCode, errorutil.FileDownload_genericError) - require.NotNil(t, err) - require.EqualError(t, err, "failed to create http request: expected error") + _, _, ewc:= download.Download(testctx, new(badDownloader)) + require.Equal(t, ewc.ErrorCode, errorutil.FileDownload_genericError) + require.NotNil(t, ewc.Err) + require.EqualError(t, ewc.Err, "failed to create http request: expected error") } func TestDownload_wrapsHTTPError(t *testing.T) { - _, _, errCode, err := download.Download(testctx, download.NewURLDownload("bad url")) - require.Equal(t, errCode, errorutil.FileDownload_unknownError) - require.NotNil(t, err) - require.Contains(t, err.Error(), "http request failed:") + _, _, ewc := download.Download(testctx, download.NewURLDownload("bad url")) + require.Equal(t, ewc.ErrorCode, errorutil.FileDownload_unknownError) + require.NotNil(t, ewc.Err) + require.Contains(t, ewc.Err.Error(), "http request failed:") } // This test is only to make sure that formatting of error messages for specific codes is correct @@ -56,25 +56,25 @@ func TestDownload_wrapsCommonErrorCodes(t *testing.T) { http.StatusBadRequest, http.StatusUnauthorized, } { - respCode, _, errCode, err := download.Download(testctx, download.NewURLDownload(fmt.Sprintf("%s/status/%d", srv.URL, code))) - require.NotNil(t, err, "not failed for code:%d", code) + respCode, _, ewc:= download.Download(testctx, download.NewURLDownload(fmt.Sprintf("%s/status/%d", srv.URL, code))) + require.NotNil(t, ewc.Err, "not failed for code:%d", code) require.Equal(t, code, respCode) switch respCode { case http.StatusNotFound: - require.Equal(t, errCode, errorutil.FileDownload_doesNotExist) - require.Contains(t, err.Error(), "because it does not exist") + require.Equal(t, ewc.ErrorCode, errorutil.FileDownload_doesNotExist) + require.Contains(t, ewc.Err.Error(), "because it does not exist") case http.StatusForbidden: - require.Equal(t, errCode, errorutil.FileDownload_networkingError) - require.Contains(t, err.Error(), "Please verify the machine has network connectivity") + require.Equal(t, ewc.ErrorCode, errorutil.FileDownload_networkingError) + require.Contains(t, ewc.Err.Error(), "Please verify the machine has network connectivity") case http.StatusInternalServerError: - require.Equal(t, errCode, errorutil.Storage_internalServerError) - require.Contains(t, err.Error(), "due to an issue with storage") + require.Equal(t, ewc.ErrorCode, errorutil.Storage_internalServerError) + require.Contains(t, ewc.Err.Error(), "due to an issue with storage") case http.StatusBadRequest: - require.Equal(t, errCode, errorutil.FileDownload_badRequest) - require.Contains(t, err.Error(), "because parts of the request were incorrectly formatted, missing, and/or invalid") + require.Equal(t, ewc.ErrorCode, errorutil.FileDownload_badRequest) + require.Contains(t, ewc.Err.Error(), "because parts of the request were incorrectly formatted, missing, and/or invalid") case http.StatusUnauthorized: - require.Equal(t, errCode, errorutil.FileDownload_accessDenied) - require.Contains(t, err.Error(), "because access was denied") + require.Equal(t, ewc.ErrorCode, errorutil.FileDownload_accessDenied) + require.Contains(t, ewc.Err.Error(), "because access was denied") } } } @@ -83,9 +83,9 @@ func TestDownload_statusOKSucceeds(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - _, body, errCode, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/status/200")) - require.Equal(t, errCode, errorutil.NoError) - require.Nil(t, err) + _, body, ewc := download.Download(testctx, download.NewURLDownload(srv.URL+"/status/200")) + require.Equal(t, ewc.ErrorCode, errorutil.NoError) + require.Nil(t, ewc.Err) defer body.Close() require.NotNil(t, body) } @@ -99,28 +99,42 @@ func TestDowload_msiDownloaderErrorMessage(t *testing.T) { msiDownloader404 := download.NewBlobWithMsiDownload(srv.URL+"/status/404", mockMsiProvider) - returnCode, body, errCode, err := download.Download(testctx, msiDownloader404) - require.Equal(t, errCode, errorutil.Msi_notFound) - require.True(t, strings.Contains(err.Error(), download.MsiDownload404ErrorString), "error string doesn't contain the correct message") + returnCode, body, ewc := download.Download(testctx, msiDownloader404) + require.Equal(t, ewc.ErrorCode, errorutil.Msi_notFound) + require.True(t, strings.Contains(ewc.Err.Error(), download.MsiDownload404ErrorString), "error string doesn't contain the correct message") require.Nil(t, body, "body is not nil for failed download") require.Equal(t, 404, returnCode, "return code was not 404") msiDownloader403 := download.NewBlobWithMsiDownload(srv.URL+"/status/403", mockMsiProvider) - returnCode, body, errCode, err = download.Download(testctx, msiDownloader403) - require.Equal(t, errCode, errorutil.Msi_doesNotHaveRightPermissions) - require.True(t, strings.Contains(err.Error(), download.MsiDownload403ErrorString), "error string doesn't contain the correct message") + returnCode, body, ewc = download.Download(testctx, msiDownloader403) + require.Equal(t, ewc.ErrorCode, errorutil.Msi_doesNotHaveRightPermissions) + require.True(t, strings.Contains(ewc.Err.Error(), download.MsiDownload403ErrorString), "error string doesn't contain the correct message") require.Nil(t, body, "body is not nil for failed download") require.Equal(t, 403, returnCode, "return code was not 403") + msiDownloade500 := download.NewBlobWithMsiDownload(srv.URL+"/status/500", mockMsiProvider) + returnCode, body, ewc = download.Download(testctx, msiDownloade500) + require.Equal(t, ewc.ErrorCode, errorutil.Imds_internalMsiError) + require.True(t, strings.Contains(ewc.Err.Error(), download.MsiDownload500ErrorString), "error string doesn't contain the correct message") + require.Nil(t, body, "body is not nil for failed download") + require.Equal(t, 500, returnCode, "return code was not 500") + + msiDownloader400 := download.NewBlobWithMsiDownload(srv.URL+"/status/400", mockMsiProvider) + returnCode, body, ewc = download.Download(testctx, msiDownloader400) + require.Equal(t, ewc.ErrorCode, errorutil.Msi_GenericRetrievalError) + require.True(t, strings.Contains(ewc.Err.Error(), download.MsiDownloadGenericErrorString), "error string doesn't contain the correct message") + require.Nil(t, body, "body is not nil for failed download") + require.Equal(t, 400, returnCode, "return code was not 400") + } func TestDownload_retrievesBody(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - _, body, errCode, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/bytes/65536")) - require.Equal(t, errCode, errorutil.NoError) - require.Nil(t, err) + _, body, ewc := download.Download(testctx, download.NewURLDownload(srv.URL+"/bytes/65536")) + require.Equal(t, ewc.ErrorCode, errorutil.NoError) + require.Nil(t, ewc.Err) defer body.Close() b, err := io.ReadAll(body) require.Nil(t, err) @@ -131,8 +145,8 @@ func TestDownload_bodyClosesWithoutError(t *testing.T) { srv := httptest.NewServer(httpbin.GetMux()) defer srv.Close() - _, body, errCode, err := download.Download(testctx, download.NewURLDownload(srv.URL+"/get")) - require.Equal(t, errCode, errorutil.NoError) - require.Nil(t, err) + _, body, ewc := download.Download(testctx, download.NewURLDownload(srv.URL+"/get")) + require.Equal(t, ewc.ErrorCode, errorutil.NoError) + require.Nil(t, ewc.Err) require.Nil(t, body.Close(), "body should close fine") } diff --git a/pkg/download/retry.go b/pkg/download/retry.go index 74f5a35..fd8bab3 100644 --- a/pkg/download/retry.go +++ b/pkg/download/retry.go @@ -9,6 +9,8 @@ import ( "time" "github.com/go-kit/kit/log" + "github.com/Azure/azure-extension-platform/vmextension" + errorutil "github.com/Azure/custom-script-extension-linux/pkg/errorutil" ) @@ -35,7 +37,7 @@ const ( // closed on failures). If the retries do not succeed, the last error is returned. // // It sleeps in exponentially increasing durations between retries. -func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf SleepFunc) (int64, int, error) { +func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf SleepFunc) (int64, vmextension.ErrorWithClarification) { var lastErr error var lastErrCode int for _, d := range downloaders { @@ -46,8 +48,8 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee lastErr = nil lastErrCode = errorutil.NoError start := time.Now() - status, out, errCode, err := Download(ctx, d) - if err == nil { + status, out, ewc := Download(ctx, d) + if ewc.Err == nil { // server returned status code 200 OK // we have a response body, copy it to the file nBytes, innerErr := io.CopyBuffer(f, out, make([]byte, writeBufSize)) @@ -57,7 +59,7 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee out.Close() end := time.Since(start) ctx.Log("info", fmt.Sprintf("file download sucessful: downloaded and saved %d bytes in %d milliseconds", nBytes, end.Milliseconds())) - return nBytes, lastErrCode, lastErr + return nBytes, vmextension.NewErrorWithClarification(lastErrCode, lastErr) } else { // we failed to download the response body and write it to file // because either connection was closed prematurely or file write operation failed @@ -71,8 +73,8 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee } } else { // cache the outer error - lastErr = err - lastErrCode = errCode + lastErr = ewc.Err + lastErrCode = ewc.ErrorCode } // we are here because either server returned a non-200 status code @@ -100,7 +102,7 @@ func WithRetries(ctx *log.Context, f *os.File, downloaders []Downloader, sf Slee } } } - return 0, lastErrCode, lastErr + return 0, vmextension.NewErrorWithClarification(lastErrCode, lastErr) } func isTransientHttpStatusCode(statusCode int) bool { diff --git a/pkg/download/retry_test.go b/pkg/download/retry_test.go index 605e2d0..7bfe869 100644 --- a/pkg/download/retry_test.go +++ b/pkg/download/retry_test.go @@ -48,7 +48,7 @@ func TestWithRetries_noRetries(t *testing.T) { sr := new(sleepRecorder) n, err := download.WithRetries(nopLog(), file, []download.Downloader{d}, sr.Sleep) - require.Nil(t, err, "should not fail") + require.Nil(t, err.Err, "should not fail") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.Equal(t, []time.Duration(nil), []time.Duration(*sr), "sleep should not be called") } @@ -65,7 +65,7 @@ func TestWithRetries_failing_validateNumberOfCalls(t *testing.T) { sr := new(sleepRecorder) n, err := download.WithRetries(nopLog(), file, []download.Downloader{bd}, sr.Sleep) - require.Contains(t, err.Error(), "expected error", "error is preserved") + require.Contains(t, err.Err.Error(), "expected error", "error is preserved") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.EqualValues(t, 7, bd.calls, "calls exactly expRetryN times") } @@ -82,8 +82,8 @@ func TestWithRetries_failingBadStatusCode_validateSleeps(t *testing.T) { sr := new(sleepRecorder) n, err := download.WithRetries(nopLog(), file, []download.Downloader{d}, sr.Sleep) - require.Contains(t, err.Error(), "429 Too Many Requests") - require.Contains(t, err.Error(), "Please verify the machine has network connectivity") + require.Contains(t, err.Err.Error(), "429 Too Many Requests") + require.Contains(t, err.Err.Error(), "Please verify the machine has network connectivity") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.Equal(t, sleepSchedule, []time.Duration(*sr)) } @@ -100,7 +100,7 @@ func TestWithRetries_healingServer(t *testing.T) { sr := new(sleepRecorder) n, err := download.WithRetries(nopLog(), file, []download.Downloader{d}, sr.Sleep) - require.Nil(t, err, "should eventually succeed") + require.Nil(t, err.Err, "should eventually succeed") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.Equal(t, sleepSchedule[:3], []time.Duration(*sr)) } @@ -118,7 +118,7 @@ func TestRetriesWith_SwitchDownloaderOn404(t *testing.T) { d200 := mockDownloader{0, hSvr.URL} n, err := download.WithRetries(nopLog(), file, []download.Downloader{&d404, &d200}, func(d time.Duration) { return }) - require.Nil(t, err, "should eventually succeed") + require.Nil(t, err.Err, "should eventually succeed") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.Equal(t, d404.timesCalled, 1) require.Equal(t, d200.timesCalled, 4) @@ -140,7 +140,7 @@ func TestRetriesWith_SwitchDownloaderThenFailWithCorretErrorMessage(t *testing.T msiDownloader403 := download.NewBlobWithMsiDownload(svr.URL+"/status/403", mockMsiProvider) n, err := download.WithRetries(nopLog(), file, []download.Downloader{&d404, msiDownloader403}, func(d time.Duration) { return }) - require.NotNil(t, err, "download with retries should fail") + require.NotNil(t, err.Err, "download with retries should fail") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.Equal(t, d404.timesCalled, 1) require.True(t, strings.Contains(err.Error(), download.MsiDownload403ErrorString), "error string doesn't contain the correct message") @@ -149,7 +149,7 @@ func TestRetriesWith_SwitchDownloaderThenFailWithCorretErrorMessage(t *testing.T msiDownloader404 := download.NewBlobWithMsiDownload(svr.URL+"/status/404", mockMsiProvider) n, err = download.WithRetries(nopLog(), file, []download.Downloader{&d404, msiDownloader404}, func(d time.Duration) { return }) - require.NotNil(t, err, "download with retries should fail") + require.NotNil(t, err.Err, "download with retries should fail") require.EqualValues(t, 0, n, "downloaded number of bytes should be zero") require.Equal(t, d404.timesCalled, 1) require.True(t, strings.Contains(err.Error(), download.MsiDownload404ErrorString), "error string doesn't contain the correct message") diff --git a/pkg/download/save.go b/pkg/download/save.go index a7cc06c..9a3dd89 100644 --- a/pkg/download/save.go +++ b/pkg/download/save.go @@ -3,6 +3,7 @@ package download import ( "os" + "github.com/Azure/azure-extension-platform/vmextension" "github.com/go-kit/kit/log" "github.com/pkg/errors" "github.com/Azure/custom-script-extension-linux/pkg/errorutil" @@ -12,17 +13,19 @@ import ( // given file. Directory of dst is not created by this function. If a file at // dst exists, it will be truncated. If a new file is created, mode is used to // set the permission bits. Written number of bytes are returned on success. -func SaveTo(ctx *log.Context, d []Downloader, dst string, mode os.FileMode) (int64, int, error) { +func SaveTo(ctx *log.Context, d []Downloader, dst string, mode os.FileMode) (int64, vmextension.ErrorWithClarification) { f, err := os.OpenFile(dst, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, mode) if err != nil { - return 0, errorutil.FileDownload_unknownError, errors.Wrap(err, "failed to open file for writing") + return 0, vmextension.NewErrorWithClarification(errorutil.FileDownload_unknownError, errors.Wrap(err, "failed to open file for writing")) + } defer f.Close() - n, errCode, err := WithRetries(ctx, f, d, ActualSleep) - if err != nil { - return n, errCode, errors.Wrapf(err, "failed to download response and write to file: %s", dst) + n, ewc := WithRetries(ctx, f, d, ActualSleep) + if ewc.Err != nil { + return n, vmextension.NewErrorWithClarification(ewc.ErrorCode, errors.Wrapf(ewc.Err, "failed to download response and write to file: %s", dst)) + } - return n, errorutil.NoError, nil + return n, vmextension.NewErrorWithClarification(errorutil.NoError, nil) } diff --git a/pkg/download/save_test.go b/pkg/download/save_test.go index 3bbfb9f..3b16de3 100644 --- a/pkg/download/save_test.go +++ b/pkg/download/save_test.go @@ -33,8 +33,8 @@ func TestSave(t *testing.T) { d := download.NewURLDownload(srv.URL + "/bytes/65536") path := filepath.Join(dir, "test-file") - n, err := download.SaveTo(nopLog(), []download.Downloader{d}, path, 0600) - require.Nil(t, err) + n, ewc := download.SaveTo(nopLog(), []download.Downloader{d}, path, 0600) + require.Nil(t, ewc.Err) require.EqualValues(t, 65536, n) fi, err := os.Stat(path) @@ -52,10 +52,10 @@ func TestSave_truncates(t *testing.T) { defer os.RemoveAll(dir) path := filepath.Join(dir, "test-file") - _, err = download.SaveTo(nopLog(), []download.Downloader{download.NewURLDownload(srv.URL + "/bytes/65536")}, path, 0600) - require.Nil(t, err) - _, err = download.SaveTo(nopLog(), []download.Downloader{download.NewURLDownload(srv.URL + "/bytes/128")}, path, 0777) - require.Nil(t, err) + _, ewc := download.SaveTo(nopLog(), []download.Downloader{download.NewURLDownload(srv.URL + "/bytes/65536")}, path, 0600) + require.Nil(t, ewc.Err) + _, ewc = download.SaveTo(nopLog(), []download.Downloader{download.NewURLDownload(srv.URL + "/bytes/128")}, path, 0777) + require.Nil(t, ewc.Err) fi, err := os.Stat(path) require.Nil(t, err) @@ -74,8 +74,8 @@ func TestSave_largeFile(t *testing.T) { size := 1024 * 1024 * 128 // 128 mb path := filepath.Join(dir, "large-file") - n, err := download.SaveTo(nopLog(), []download.Downloader{download.NewURLDownload(srv.URL + "/bytes/" + fmt.Sprintf("%d", size))}, path, 0600) - require.Nil(t, err) + n, ewc := download.SaveTo(nopLog(), []download.Downloader{download.NewURLDownload(srv.URL + "/bytes/" + fmt.Sprintf("%d", size))}, path, 0600) + require.Nil(t, ewc.Err) require.EqualValues(t, size, n) fi, err := os.Stat(path)