From 342f17e74cdee8fa826a7d7c91acd065a2c3a965 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 1 Sep 2023 18:02:18 +0100 Subject: [PATCH 001/115] feat(authenticate): support pkce --- go.mod | 13 ++ go.sum | 74 +++++++ pkg/app/commands.go | 3 + pkg/app/run_test.go | 1 + pkg/commands/authenticate/doc.go | 3 + pkg/commands/authenticate/root.go | 334 ++++++++++++++++++++++++++++++ 6 files changed, 428 insertions(+) create mode 100644 pkg/commands/authenticate/doc.go create mode 100644 pkg/commands/authenticate/root.go diff --git a/go.mod b/go.mod index 034f807e5..cacbf7f9c 100644 --- a/go.mod +++ b/go.mod @@ -28,10 +28,12 @@ require ( require ( github.com/fastly/go-fastly/v8 v8.6.4 + github.com/hashicorp/cap v0.3.4 github.com/kennygrant/sanitize v1.2.4 github.com/mholt/archiver v3.1.1+incompatible github.com/otiai10/copy v1.14.0 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 + github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/theckman/yacspin v0.13.12 golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20231006140011-7918f672742d @@ -47,11 +49,17 @@ require ( require ( github.com/andybalholm/brotli v1.0.5 // indirect + github.com/coreos/go-oidc/v3 v3.5.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/jsonapi v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/klauspost/compress v1.16.6 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -59,11 +67,16 @@ require ( github.com/nwaples/rardecode v1.1.2 // indirect github.com/peterhellberg/link v1.1.0 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.2 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.5.0 // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/text v0.14.0 + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6ab0aaa4e..64ea3a984 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= @@ -9,6 +10,8 @@ github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgEHw= +github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -24,26 +27,41 @@ github.com/fastly/go-fastly/v8 v8.6.4 h1:/jf1j8VwpXDR+b7PA6RBhBcRo3OoEWFwFHFeOba github.com/fastly/go-fastly/v8 v8.6.4/go.mod h1:sC3WMOjQxwXk+gRI68ooTJJsI+/6AMA/4JLvrhNgpVk= github.com/fastly/kingpin v2.1.12-0.20191105091915-95d230a53780+incompatible h1:FhrXlfhgGCS+uc6YwyiFUt04alnjpoX7vgDKJxS6Qbk= github.com/fastly/kingpin v2.1.12-0.20191105091915-95d230a53780+incompatible/go.mod h1:U8UynVoU1SQaqD2I4ZqgYd5lx3A1ipQYn4aSt2Y5h6c= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8= github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/jsonapi v1.0.0 h1:qIGgO5Smu3yJmSs+QlvhQnrscdZfFhiV6S8ryJAglqU= github.com/google/jsonapi v1.0.0/go.mod h1:YYHiRPJT8ARXGER8In9VuLv4qvLfDmA9ULQqptbLE4s= +github.com/hashicorp/cap v0.3.4 h1:RoqWYqr6LaDLuvnBCpod1sZtvuEhehIhu0GncmoHW40= +github.com/hashicorp/cap v0.3.4/go.mod h1:dHTmyMIVbzT981XxRoci5G//dfWmd/HhuNiCH6J5+IA= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -62,8 +80,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -110,9 +132,13 @@ github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDj github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= github.com/segmentio/textio v1.2.0 h1:Ug4IkV3kh72juJbG8azoSBlgebIbUUxVNrfFcKHfTSQ= github.com/segmentio/textio v1.2.0/go.mod h1:+Rb7v0YVODP+tK5F7FD9TCkV7gOYx9IgLHWiqtvY8ag= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/theckman/yacspin v0.13.12 h1:CdZ57+n0U6JMuh2xqjnjRq5Haj6v1ner2djtLQRzJr4= @@ -125,25 +151,73 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +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-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +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.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/pkg/app/commands.go b/pkg/app/commands.go index c8138e84e..abb93f9d8 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -6,6 +6,7 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/acl" "github.com/fastly/cli/pkg/commands/aclentry" + "github.com/fastly/cli/pkg/commands/authenticate" "github.com/fastly/cli/pkg/commands/authtoken" "github.com/fastly/cli/pkg/commands/backend" "github.com/fastly/cli/pkg/commands/compute" @@ -99,6 +100,7 @@ func defineCommands( aclEntryDescribe := aclentry.NewDescribeCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryList := aclentry.NewListCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryUpdate := aclentry.NewUpdateCommand(aclEntryCmdRoot.CmdClause, g, m) + authenticateCmdRoot := authenticate.NewRootCommand(app, g) authtokenCmdRoot := authtoken.NewRootCommand(app, g) authtokenCreate := authtoken.NewCreateCommand(authtokenCmdRoot.CmdClause, g, m) authtokenDelete := authtoken.NewDeleteCommand(authtokenCmdRoot.CmdClause, g, m) @@ -465,6 +467,7 @@ func defineCommands( aclEntryDescribe, aclEntryList, aclEntryUpdate, + authenticateCmdRoot, authtokenCmdRoot, authtokenCreate, authtokenDelete, diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index d82252502..9388666dd 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -59,6 +59,7 @@ complete -F _fastly_bash_autocomplete fastly WantOutput: `help acl acl-entry +authenticate auth-token backend compute diff --git a/pkg/commands/authenticate/doc.go b/pkg/commands/authenticate/doc.go new file mode 100644 index 000000000..e688c1ee6 --- /dev/null +++ b/pkg/commands/authenticate/doc.go @@ -0,0 +1,3 @@ +// Package authenticate contains commands to authenticate with Fastly and to +// acquire a temporary API token, which will be auto-rotated. +package authenticate diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go new file mode 100644 index 000000000..add03cdbb --- /dev/null +++ b/pkg/commands/authenticate/root.go @@ -0,0 +1,334 @@ +package authenticate + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/hashicorp/cap/jwt" + "github.com/hashicorp/cap/oidc" + "github.com/skratchdot/open-golang/open" + + "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/cli/pkg/config" + fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/profile" + "github.com/fastly/cli/pkg/text" +) + +// FIXME: DELETE👇 +// NOTE: https://keycloak.ext.awsuse2.dev.k8s.secretcdn.net/realms/fastly/.well-known/openid-configuration + +// RootCommand is the parent command for all subcommands in this package. +// It should be installed under the primary root command. +type RootCommand struct { + cmd.Base +} + +// AuthRemediation is a generic remediation message for an error authorizing. +const AuthRemediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" + +// AuthProviderCLIAppURL is the auth provider's device code URL. +// FIXME: Add --accounts override (accounts.fastly.com) +const AuthProviderCLIAppURL = "https://accounts.secretcdn-stg.net" + +// AuthProviderClientID is the auth provider's Client ID. +const AuthProviderClientID = "fastly-cli" + +// AuthProviderAudience is the unique identifier of the API your app wants to access. +// FIXME: Use --endpoint override (api.fastly.com) +const AuthProviderAudience = "https://api.secretcdn-stg.net/" + +// AuthProviderRedirectURL is the endpoint the auth provider will pass an authorization code to. +const AuthProviderRedirectURL = "http://localhost:8080/callback" + +// NewRootCommand returns a new command registered in the parent. +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { + var c RootCommand + c.Globals = g + c.CmdClause = parent.Command("authenticate", "Authenticate with Fastly (returns temporary, auto-rotated, API token)") + return &c +} + +// Exec implements the command interface. +func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { + verifier, err := oidc.NewCodeVerifier() + if err != nil { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to generate a code verifier: %w", err), + Remediation: AuthRemediation, + } + } + + result := make(chan authorizationResult) + + s := server{ + result: result, + router: http.NewServeMux(), + verifier: verifier, + } + s.routes() + + var serverErr error + + go func() { + err := s.startServer() + if err != nil { + serverErr = err + } + }() + + if serverErr != nil { + return serverErr + } + + text.Info(out, "Starting localhost server to handle the authentication flow.") + + authorizationURL, err := generateAuthorizationURL(verifier) + if err != nil { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to generate an authorization URL: %w", err), + Remediation: AuthRemediation, + } + } + + text.Break(out) + text.Description(out, "We're opening the following URL in your default web browser so you may authenticate with Fastly", authorizationURL) + + err = open.Run(authorizationURL) + if err != nil { + return fmt.Errorf("failed to open your default browser: %w", err) + } + + ar := <-result + if ar.err != nil || ar.sessionToken == "" { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to authorize: %w", ar.err), + Remediation: AuthRemediation, + } + } + + text.Success(out, "Session token (persisted to your local configuration): %s", ar.sessionToken) + + profileName, _ := profile.Default(c.Globals.Config.Profiles) + if profileName == "" { + // FIXME: Return a more appropriate remediation. + return fsterr.RemediationError{ + Inner: fmt.Errorf("no profiles available"), + Remediation: fsterr.ProfileRemediation, + } + } + + ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, func(p *config.Profile) { + p.Token = ar.sessionToken + }) + if !ok { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update default profile with new session token"), + Remediation: "Run `fastly profile update` and manually paste in the session token.", + } + } + c.Globals.Config.Profiles = ps + + if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { + c.Globals.ErrLog.Add(err) + return fmt.Errorf("error saving config file: %w", err) + } + + // FIXME: Don't just update the default profile. + // Allow user to configure this via a --profile flag. + + return nil +} + +type server struct { + result chan authorizationResult + router *http.ServeMux + verifier *oidc.S256Verifier +} + +func (s *server) startServer() error { + // TODO: Consider using a random port to avoid local network conflicts. + // Chat with authentication provider about how to use a random port. + server := &http.Server{ + Addr: ":8080", + Handler: s.router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + err := server.ListenAndServe() + if err != nil { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to start local server: %w", err), + Remediation: AuthRemediation, + } + } + return nil +} + +func (s *server) routes() { + s.router.HandleFunc("/callback", s.handleCallback()) +} + +func (s *server) handleCallback() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + authorizationCode := r.URL.Query().Get("code") + if authorizationCode == "" { + fmt.Fprint(w, "ERROR: no authorization code returned\n") + s.result <- authorizationResult{ + err: fmt.Errorf("no authorization code returned"), + } + return + } + + // Exchange the authorization code and the code verifier for a JWT. + // NOTE: I use the identifier `j` to avoid overlap with the `jwt` package. + codeVerifier := s.verifier.Verifier() + j, err := getJWT(codeVerifier, authorizationCode) + if err != nil || j.AccessToken == "" || j.IDToken == "" { + fmt.Fprint(w, "ERROR: failed to exchange code for JWT\n") + s.result <- authorizationResult{ + err: fmt.Errorf("failed to exchange code for JWT"), + } + return + } + + claims, err := verifyJWTSignature(j.AccessToken) + if err != nil { + s.result <- authorizationResult{ + err: err, + } + return + } + + fmt.Printf("jwt: %+v\n", j) + fmt.Printf("claims: %+v\n", claims) + + sessionToken, err := extractSessionToken(claims) + if err != nil { + s.result <- authorizationResult{ + err: err, + } + return + } + + fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") + s.result <- authorizationResult{ + jwt: j, + sessionToken: sessionToken, + } + } +} + +type authorizationResult struct { + err error + jwt JWT + sessionToken string +} + +func generateAuthorizationURL(verifier *oidc.S256Verifier) (string, error) { + challenge, err := oidc.CreateCodeChallenge(verifier) + if err != nil { + return "", err + } + + authorizationURL := fmt.Sprintf( + "%s/realms/fastly/protocol/openid-connect/auth?audience=%s"+ + "&scope=openid"+ + "&response_type=code&client_id=%s"+ + "&code_challenge=%s"+ + "&code_challenge_method=S256&redirect_uri=%s", + AuthProviderCLIAppURL, AuthProviderAudience, AuthProviderClientID, challenge, AuthProviderRedirectURL) + + return authorizationURL, nil +} + +func getJWT(codeVerifier, authorizationCode string) (JWT, error) { + path := "/realms/fastly/protocol/openid-connect/token" + + payload := fmt.Sprintf( + "grant_type=authorization_code&client_id=%s&code_verifier=%s&code=%s&redirect_uri=%s", + AuthProviderClientID, + codeVerifier, + authorizationCode, + "http://localhost:8080/callback", // NOTE: not redirected to, just a security check. + ) + + req, err := http.NewRequest("POST", AuthProviderCLIAppURL+path, strings.NewReader(payload)) + if err != nil { + return JWT{}, err + } + + req.Header.Add("content-type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return JWT{}, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return JWT{}, fmt.Errorf("failed to exchange code for jwt (status: %s)", res.Status) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return JWT{}, err + } + + var j JWT + err = json.Unmarshal(body, &j) + if err != nil { + return JWT{}, err + } + + return j, nil +} + +// JWT is the API response for a Token request. +type JWT struct { + // AccessToken can be exchanged for a Fastly API token. + AccessToken string `json:"access_token"` + // ExpiresIn indicates the lifetime (in seconds) of the access token. + ExpiresIn int `json:"expires_in"` + // IDToken contains user information that must be decoded and extracted. + IDToken string `json:"id_token"` + // TokenType indicates which HTTP authentication scheme is used (e.g. Bearer). + TokenType string `json:"token_type"` +} + +func verifyJWTSignature(token string) (claims map[string]any, err error) { + ctx := context.Background() + path := "/realms/fastly/protocol/openid-connect/certs" + + // NOTE: The last argument is optional and is for validating the JWKs endpoint + // (which we don't need to do, so we pass an empty string) + keySet, err := jwt.NewJSONWebKeySet(ctx, AuthProviderCLIAppURL+path, "") + if err != nil { + return claims, fmt.Errorf("failed to verify signature of access token: %w", err) + } + + claims, err = keySet.VerifySignature(ctx, token) + if err != nil { + return nil, fmt.Errorf("failed to verify signature of access token: %w", err) + } + + return claims, nil +} + +func extractSessionToken(claims map[string]any) (string, error) { + if i, ok := claims["legacy_session_token"]; ok { + if t, ok := i.(string); ok { + if t != "" { + return t, nil + } + } + } + return "", fmt.Errorf("failed to extract session token from JWT custom claim") +} From 00e522a17380629997f4b8408f12a08e68c926fe Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 4 Sep 2023 17:29:06 +0100 Subject: [PATCH 002/115] refactor: authentication --- pkg/app/run.go | 40 ++++ pkg/auth/auth.go | 291 ++++++++++++++++++++++++++++++ pkg/auth/doc.go | 2 + pkg/commands/authenticate/root.go | 273 ++++------------------------ pkg/commands/profile/create.go | 2 +- pkg/profile/profile.go | 3 + 6 files changed, 368 insertions(+), 243 deletions(-) create mode 100644 pkg/auth/auth.go create mode 100644 pkg/auth/doc.go diff --git a/pkg/app/run.go b/pkg/app/run.go index ec0e60403..68655d112 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -136,6 +136,37 @@ func Run(opts RunOpts) error { token, source := g.Token() + // Ensure the user has configured an API token, otherwise trigger the + // authentication flow (unless calling one of the profile commands). + if source == lookup.SourceUndefined && !allowNoToken(commandName) { + for _, command := range commands { + if command.Name() == "authenticate" { + text.Warning(opts.Stdout, "No API token could be found. We need to open your browser to authenticate you.") + text.Break(opts.Stdout) + cont, err := text.AskYesNo(opts.Stdout, "Are you sure you want to continue? [yes/no]: ", opts.Stdin) + if err != nil { + return err + } + if !cont { + return nil + } + text.Break(opts.Stdout) + + err = command.Exec(opts.Stdin, opts.Stdout) + if err != nil { + return fmt.Errorf("failed to authenticate: %w", err) + } + break + } + } + + // Recheck for token (should be persisted to profile data). + token, source = g.Token() + if source == lookup.SourceUndefined { + return fsterr.ErrNoToken + } + } + if g.Verbose() { displayTokenSource( source, @@ -279,3 +310,12 @@ func commandCollectsData(command string) bool { } return false } + +// allowNoToken determines if the command to be executed is one that should work +// even if there is no prior API token available. +func allowNoToken(command string) bool { + if command == "version" || command == "whoami" || strings.HasPrefix(command, "profile ") { + return true + } + return false +} diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go new file mode 100644 index 000000000..9788b128b --- /dev/null +++ b/pkg/auth/auth.go @@ -0,0 +1,291 @@ +package auth + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/hashicorp/cap/jwt" + "github.com/hashicorp/cap/oidc" + + fsterr "github.com/fastly/cli/pkg/errors" +) + +// FIXME: UPDATE WITH PUBLIC ENDPOINT👇 +// NOTE: https://keycloak.ext.awsuse2.dev.k8s.secretcdn.net/realms/fastly/.well-known/openid-configuration + +// Remediation is a generic remediation message for an error authorizing. +const Remediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" + +// ServerURL is the auth provider's device code URL. +// FIXME: Add --accounts override (accounts.fastly.com) +const ServerURL = "https://accounts.secretcdn-stg.net" + +// ClientID is the auth provider's Client ID. +const ClientID = "fastly-cli" + +// Audience is the unique identifier of the API your app wants to access. +// FIXME: Use --endpoint override (api.fastly.com) +const Audience = "https://api.secretcdn-stg.net/" + +// RedirectURL is the endpoint the auth provider will pass an authorization code to. +const RedirectURL = "http://localhost:8080/callback" + +type Server struct { + Result chan AuthorizationResult + Router *http.ServeMux + Verifier *oidc.S256Verifier +} + +func (s *Server) Start() error { + server := &http.Server{ + Addr: ":8080", + Handler: s.Router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + err := server.ListenAndServe() + if err != nil { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to start local server: %w", err), + Remediation: Remediation, + } + } + return nil +} + +func (s *Server) Routes() { + s.Router.HandleFunc("/callback", s.handleCallback()) +} + +func (s *Server) handleCallback() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + authorizationCode := r.URL.Query().Get("code") + if authorizationCode == "" { + fmt.Fprint(w, "ERROR: no authorization code returned\n") + s.Result <- AuthorizationResult{ + Err: fmt.Errorf("no authorization code returned"), + } + return + } + + // Exchange the authorization code and the code verifier for a JWT. + // NOTE: I use the identifier `j` to avoid overlap with the `jwt` package. + codeVerifier := s.Verifier.Verifier() + j, err := GetJWT(codeVerifier, authorizationCode) + if err != nil || j.AccessToken == "" || j.IDToken == "" { + fmt.Fprint(w, "ERROR: failed to exchange code for JWT\n") + s.Result <- AuthorizationResult{ + Err: fmt.Errorf("failed to exchange code for JWT"), + } + return + } + + claims, err := verifyJWTSignature(j.AccessToken) + if err != nil { + s.Result <- AuthorizationResult{ + Err: err, + } + return + } + + fmt.Printf("jwt: %+v\n\n", j) + fmt.Printf("claims: %+v\n\n", claims) + + sessionToken, err := extractSessionToken(claims) + if err != nil { + s.Result <- AuthorizationResult{ + Err: err, + } + return + } + + email, ok := claims["email"] + if !ok { + s.Result <- AuthorizationResult{ + Err: errors.New("unable to extract email from JWT claims"), + } + return + } + + fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") + + s.Result <- AuthorizationResult{ + Email: email.(string), + Jwt: j, + SessionToken: sessionToken, + } + } +} + +type AuthorizationResult struct { + // Email address extracted from JWT claims. + Email string + // Err is any error received during authentication. + Err error + // Jwt is the JWT token returned by the authorization server. + Jwt JWT + // SessionToken is a temporary API token. + SessionToken string +} + +// GenURL constructs the required authorization_endpoint path. +func GenURL(verifier *oidc.S256Verifier) (string, error) { + challenge, err := oidc.CreateCodeChallenge(verifier) + if err != nil { + return "", err + } + + authorizationURL := fmt.Sprintf( + "%s/realms/fastly/protocol/openid-connect/auth?audience=%s"+ + "&scope=openid"+ + "&response_type=code&client_id=%s"+ + "&code_challenge=%s"+ + "&code_challenge_method=S256&redirect_uri=%s", + ServerURL, Audience, ClientID, challenge, RedirectURL) + + return authorizationURL, nil +} + +// GenJWT constructs and calls the token_endpoint path, returning a JWT +// containing the access and refresh tokens and associated TTLs. +func GetJWT(codeVerifier, authorizationCode string) (JWT, error) { + path := "/realms/fastly/protocol/openid-connect/token" + + payload := fmt.Sprintf( + "grant_type=authorization_code&client_id=%s&code_verifier=%s&code=%s&redirect_uri=%s", + ClientID, + codeVerifier, + authorizationCode, + "http://localhost:8080/callback", // NOTE: not redirected to, just a security check. + ) + + req, err := http.NewRequest("POST", ServerURL+path, strings.NewReader(payload)) + if err != nil { + return JWT{}, err + } + + req.Header.Add("content-type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return JWT{}, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return JWT{}, fmt.Errorf("failed to exchange code for jwt (status: %s)", res.Status) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return JWT{}, err + } + + fmt.Printf("body: %#v\n\n", string(body)) + + var j JWT + err = json.Unmarshal(body, &j) + if err != nil { + return JWT{}, err + } + + fmt.Printf("j: %#v\n\n", j) + + return j, nil +} + +// JWT is the API response for a Token request. +// +// Access Token typically has a TTL of 5mins. +// Refresh Token typically has a TTL of 30mins. +type JWT struct { + // AccessToken can be exchanged for a Fastly API token. + AccessToken string `json:"access_token"` + // ExpiresIn indicates the lifetime (in seconds) of the access token. + ExpiresIn int `json:"expires_in"` + // IDToken contains user information that must be decoded and extracted. + IDToken string `json:"id_token"` + // RefreshExpiresIn indicates the lifetime (in seconds) of the refresh token. + RefreshExpiresIn int `json:"refresh_expires_in"` + // RefreshToken contains a token used to refresh the issued access token. + RefreshToken string `json:"refresh_token"` + // TokenType indicates which HTTP authentication scheme is used (e.g. Bearer). + TokenType string `json:"token_type"` +} + +// verifyJWTSignature calls the jwks_uri endpoint and extracts its claims. +func verifyJWTSignature(token string) (claims map[string]any, err error) { + ctx := context.Background() + path := "/realms/fastly/protocol/openid-connect/certs" + + // NOTE: The last argument is optional and is for validating the JWKs endpoint + // (which we don't need to do, so we pass an empty string) + keySet, err := jwt.NewJSONWebKeySet(ctx, ServerURL+path, "") + if err != nil { + return claims, fmt.Errorf("failed to verify signature of access token: %w", err) + } + + claims, err = keySet.VerifySignature(ctx, token) + if err != nil { + return nil, fmt.Errorf("failed to verify signature of access token: %w", err) + } + + return claims, nil +} + +// extractSessionToken extracts a legacy session token from the given claims. +func extractSessionToken(claims map[string]any) (string, error) { + if i, ok := claims["legacy_session_token"]; ok { + if t, ok := i.(string); ok { + if t != "" { + return t, nil + } + } + } + return "", fmt.Errorf("failed to extract session token from JWT custom claim") +} + +// refreshToken constructs and calls the token_endpoint for refreshing the +// access token and returns the new access token. +func refreshToken(token string) ([]byte, error) { + path := "/realms/fastly/protocol/openid-connect/token" + + payload := fmt.Sprintf( + "grant_type=refresh_token&client_id=%s&refresh_token=%s", + ClientID, + token, + ) + + req, err := http.NewRequest("POST", ServerURL+path, strings.NewReader(payload)) + if err != nil { + return nil, err + } + + req.Header.Add("content-type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to refresh the access token (status: %s)", res.Status) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + fmt.Printf("refresh body: %#v\n\n", string(body)) + + return body, nil +} diff --git a/pkg/auth/doc.go b/pkg/auth/doc.go new file mode 100644 index 000000000..80936d07b --- /dev/null +++ b/pkg/auth/doc.go @@ -0,0 +1,2 @@ +// Package auth contains types to authenticate with Fastly. +package auth diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index add03cdbb..a724b5b7e 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -1,18 +1,14 @@ package authenticate import ( - "context" - "encoding/json" "fmt" "io" "net/http" - "strings" - "time" - "github.com/hashicorp/cap/jwt" "github.com/hashicorp/cap/oidc" "github.com/skratchdot/open-golang/open" + "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/config" fsterr "github.com/fastly/cli/pkg/errors" @@ -21,32 +17,12 @@ import ( "github.com/fastly/cli/pkg/text" ) -// FIXME: DELETE👇 -// NOTE: https://keycloak.ext.awsuse2.dev.k8s.secretcdn.net/realms/fastly/.well-known/openid-configuration - // RootCommand is the parent command for all subcommands in this package. // It should be installed under the primary root command. type RootCommand struct { cmd.Base } -// AuthRemediation is a generic remediation message for an error authorizing. -const AuthRemediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" - -// AuthProviderCLIAppURL is the auth provider's device code URL. -// FIXME: Add --accounts override (accounts.fastly.com) -const AuthProviderCLIAppURL = "https://accounts.secretcdn-stg.net" - -// AuthProviderClientID is the auth provider's Client ID. -const AuthProviderClientID = "fastly-cli" - -// AuthProviderAudience is the unique identifier of the API your app wants to access. -// FIXME: Use --endpoint override (api.fastly.com) -const AuthProviderAudience = "https://api.secretcdn-stg.net/" - -// AuthProviderRedirectURL is the endpoint the auth provider will pass an authorization code to. -const AuthProviderRedirectURL = "http://localhost:8080/callback" - // NewRootCommand returns a new command registered in the parent. func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand @@ -61,23 +37,23 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { if err != nil { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to generate a code verifier: %w", err), - Remediation: AuthRemediation, + Remediation: auth.Remediation, } } - result := make(chan authorizationResult) + result := make(chan auth.AuthorizationResult) - s := server{ - result: result, - router: http.NewServeMux(), - verifier: verifier, + s := auth.Server{ + Result: result, + Router: http.NewServeMux(), + Verifier: verifier, } - s.routes() + s.Routes() var serverErr error go func() { - err := s.startServer() + err := s.Start() if err != nil { serverErr = err } @@ -87,13 +63,13 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { return serverErr } - text.Info(out, "Starting localhost server to handle the authentication flow.") + text.Info(out, "Starting a local server to handle the authentication flow.") - authorizationURL, err := generateAuthorizationURL(verifier) + authorizationURL, err := auth.GenURL(verifier) if err != nil { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to generate an authorization URL: %w", err), - Remediation: AuthRemediation, + Remediation: auth.Remediation, } } @@ -106,34 +82,33 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { } ar := <-result - if ar.err != nil || ar.sessionToken == "" { + if ar.Err != nil || ar.SessionToken == "" { return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to authorize: %w", ar.err), - Remediation: AuthRemediation, + Inner: fmt.Errorf("failed to authorize: %w", ar.Err), + Remediation: auth.Remediation, } } - text.Success(out, "Session token (persisted to your local configuration): %s", ar.sessionToken) - profileName, _ := profile.Default(c.Globals.Config.Profiles) + if profileName == "" { - // FIXME: Return a more appropriate remediation. - return fsterr.RemediationError{ - Inner: fmt.Errorf("no profiles available"), - Remediation: fsterr.ProfileRemediation, + c.Globals.Config.Profiles[profile.DefaultName] = &config.Profile{ + Default: true, + Email: ar.Email, + Token: ar.SessionToken, } - } - - ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, func(p *config.Profile) { - p.Token = ar.sessionToken - }) - if !ok { - return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update default profile with new session token"), - Remediation: "Run `fastly profile update` and manually paste in the session token.", + } else { + ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, func(p *config.Profile) { + p.Token = ar.SessionToken + }) + if !ok { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update default profile with new session token"), + Remediation: "Run `fastly profile update` and manually paste in the session token.", + } } + c.Globals.Config.Profiles = ps } - c.Globals.Config.Profiles = ps if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { c.Globals.ErrLog.Add(err) @@ -143,192 +118,6 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { // FIXME: Don't just update the default profile. // Allow user to configure this via a --profile flag. + text.Success(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) return nil } - -type server struct { - result chan authorizationResult - router *http.ServeMux - verifier *oidc.S256Verifier -} - -func (s *server) startServer() error { - // TODO: Consider using a random port to avoid local network conflicts. - // Chat with authentication provider about how to use a random port. - server := &http.Server{ - Addr: ":8080", - Handler: s.router, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - } - - err := server.ListenAndServe() - if err != nil { - return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to start local server: %w", err), - Remediation: AuthRemediation, - } - } - return nil -} - -func (s *server) routes() { - s.router.HandleFunc("/callback", s.handleCallback()) -} - -func (s *server) handleCallback() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - authorizationCode := r.URL.Query().Get("code") - if authorizationCode == "" { - fmt.Fprint(w, "ERROR: no authorization code returned\n") - s.result <- authorizationResult{ - err: fmt.Errorf("no authorization code returned"), - } - return - } - - // Exchange the authorization code and the code verifier for a JWT. - // NOTE: I use the identifier `j` to avoid overlap with the `jwt` package. - codeVerifier := s.verifier.Verifier() - j, err := getJWT(codeVerifier, authorizationCode) - if err != nil || j.AccessToken == "" || j.IDToken == "" { - fmt.Fprint(w, "ERROR: failed to exchange code for JWT\n") - s.result <- authorizationResult{ - err: fmt.Errorf("failed to exchange code for JWT"), - } - return - } - - claims, err := verifyJWTSignature(j.AccessToken) - if err != nil { - s.result <- authorizationResult{ - err: err, - } - return - } - - fmt.Printf("jwt: %+v\n", j) - fmt.Printf("claims: %+v\n", claims) - - sessionToken, err := extractSessionToken(claims) - if err != nil { - s.result <- authorizationResult{ - err: err, - } - return - } - - fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") - s.result <- authorizationResult{ - jwt: j, - sessionToken: sessionToken, - } - } -} - -type authorizationResult struct { - err error - jwt JWT - sessionToken string -} - -func generateAuthorizationURL(verifier *oidc.S256Verifier) (string, error) { - challenge, err := oidc.CreateCodeChallenge(verifier) - if err != nil { - return "", err - } - - authorizationURL := fmt.Sprintf( - "%s/realms/fastly/protocol/openid-connect/auth?audience=%s"+ - "&scope=openid"+ - "&response_type=code&client_id=%s"+ - "&code_challenge=%s"+ - "&code_challenge_method=S256&redirect_uri=%s", - AuthProviderCLIAppURL, AuthProviderAudience, AuthProviderClientID, challenge, AuthProviderRedirectURL) - - return authorizationURL, nil -} - -func getJWT(codeVerifier, authorizationCode string) (JWT, error) { - path := "/realms/fastly/protocol/openid-connect/token" - - payload := fmt.Sprintf( - "grant_type=authorization_code&client_id=%s&code_verifier=%s&code=%s&redirect_uri=%s", - AuthProviderClientID, - codeVerifier, - authorizationCode, - "http://localhost:8080/callback", // NOTE: not redirected to, just a security check. - ) - - req, err := http.NewRequest("POST", AuthProviderCLIAppURL+path, strings.NewReader(payload)) - if err != nil { - return JWT{}, err - } - - req.Header.Add("content-type", "application/x-www-form-urlencoded") - - res, err := http.DefaultClient.Do(req) - if err != nil { - return JWT{}, err - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return JWT{}, fmt.Errorf("failed to exchange code for jwt (status: %s)", res.Status) - } - - body, err := io.ReadAll(res.Body) - if err != nil { - return JWT{}, err - } - - var j JWT - err = json.Unmarshal(body, &j) - if err != nil { - return JWT{}, err - } - - return j, nil -} - -// JWT is the API response for a Token request. -type JWT struct { - // AccessToken can be exchanged for a Fastly API token. - AccessToken string `json:"access_token"` - // ExpiresIn indicates the lifetime (in seconds) of the access token. - ExpiresIn int `json:"expires_in"` - // IDToken contains user information that must be decoded and extracted. - IDToken string `json:"id_token"` - // TokenType indicates which HTTP authentication scheme is used (e.g. Bearer). - TokenType string `json:"token_type"` -} - -func verifyJWTSignature(token string) (claims map[string]any, err error) { - ctx := context.Background() - path := "/realms/fastly/protocol/openid-connect/certs" - - // NOTE: The last argument is optional and is for validating the JWKs endpoint - // (which we don't need to do, so we pass an empty string) - keySet, err := jwt.NewJSONWebKeySet(ctx, AuthProviderCLIAppURL+path, "") - if err != nil { - return claims, fmt.Errorf("failed to verify signature of access token: %w", err) - } - - claims, err = keySet.VerifySignature(ctx, token) - if err != nil { - return nil, fmt.Errorf("failed to verify signature of access token: %w", err) - } - - return claims, nil -} - -func extractSessionToken(claims map[string]any) (string, error) { - if i, ok := claims["legacy_session_token"]; ok { - if t, ok := i.(string); ok { - if t != "" { - return t, nil - } - } - } - return "", fmt.Errorf("failed to extract session token from JWT custom claim") -} diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 1685c5696..5eb1320b4 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -34,7 +34,7 @@ func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data var c CreateCommand c.Globals = g c.CmdClause = parent.Command("create", "Create user profile") - c.CmdClause.Arg("profile", "Profile to create (default 'user')").Default("user").Short('p').StringVar(&c.profile) + c.CmdClause.Arg("profile", "Profile to create (default 'user')").Default(profile.DefaultName).Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) c.clientFactory = cf return &c diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index dc5ed7e2a..7946ae7d6 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -13,6 +13,9 @@ import ( "github.com/fastly/cli/pkg/text" ) +// DefaultName is the default profile name. +const DefaultName = "user" + // DoesNotExist describes an output error/warning message. const DoesNotExist = "the profile '%s' does not exist" From 4c6afbc1c92bc148ea07c5c1816751d897ed709e Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 4 Sep 2023 19:09:07 +0100 Subject: [PATCH 003/115] fix: acquire session token from api endpoint --- pkg/api/undocumented/undocumented.go | 4 ++- pkg/app/run.go | 2 ++ pkg/auth/auth.go | 46 ++++++++++++++++++++++++++-- pkg/commands/authenticate/root.go | 9 ++++-- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index da782bc09..4971d8d28 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -67,7 +67,9 @@ func Call(opts CallOptions) (data []byte, err error) { return data, NewError(err, 0) } - req.Header.Set("Fastly-Key", opts.Token) + if opts.Token != "" { + req.Header.Set("Fastly-Key", opts.Token) + } req.Header.Set("User-Agent", useragent.Name) for _, header := range opts.HTTPHeaders { req.Header.Set(header.Key, header.Value) diff --git a/pkg/app/run.go b/pkg/app/run.go index 68655d112..22f03ab96 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -136,6 +136,8 @@ func Run(opts RunOpts) error { token, source := g.Token() + // FIXME: Identify token expiry! + // Ensure the user has configured an API token, otherwise trigger the // authentication flow (unless calling one of the profile commands). if source == lookup.SourceUndefined && !allowNoToken(commandName) { diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 9788b128b..6a3f3747b 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -13,6 +13,8 @@ import ( "github.com/hashicorp/cap/jwt" "github.com/hashicorp/cap/oidc" + "github.com/fastly/cli/pkg/api" + "github.com/fastly/cli/pkg/api/undocumented" fsterr "github.com/fastly/cli/pkg/errors" ) @@ -36,12 +38,22 @@ const Audience = "https://api.secretcdn-stg.net/" // RedirectURL is the endpoint the auth provider will pass an authorization code to. const RedirectURL = "http://localhost:8080/callback" +// Server is a local server responsible for authentication processing. type Server struct { - Result chan AuthorizationResult - Router *http.ServeMux + // HTTPClient is a HTTP client used to call the API to exchange the access + // token for a session token. + HTTPClient api.HTTPClient + // Result is a channel that reports the result of authorization. + Result chan AuthorizationResult + // Router is an HTTP request multiplexer. + Router *http.ServeMux + // Verifier represents an OAuth PKCE code verifier that uses the S256 challenge method Verifier *oidc.S256Verifier + // SessionEndpoint is the API endpoint host. + SessionEndpoint string } +// Start starts a local server for handling authentication processing. func (s *Server) Start() error { server := &http.Server{ Addr: ":8080", @@ -60,6 +72,7 @@ func (s *Server) Start() error { return nil } +// Routes configures the callback handler. func (s *Server) Routes() { s.Router.HandleFunc("/callback", s.handleCallback()) } @@ -98,6 +111,34 @@ func (s *Server) handleCallback() http.HandlerFunc { fmt.Printf("jwt: %+v\n\n", j) fmt.Printf("claims: %+v\n\n", claims) + resp, err := undocumented.Call(undocumented.CallOptions{ + APIEndpoint: s.SessionEndpoint, + HTTPClient: s.HTTPClient, + HTTPHeaders: []undocumented.HTTPHeader{ + { + Key: "Authorization", + Value: fmt.Sprintf("Bearer %s", j.AccessToken), + }, + }, + Method: http.MethodPost, + Path: "/login-enhanced", + }) + if err != nil { + fmt.Printf("error response from undocumented call: %#v\n", string(resp)) + if apiErr, ok := err.(undocumented.APIError); ok { + if apiErr.StatusCode != http.StatusConflict { + err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) + } + } + s.Result <- AuthorizationResult{ + Err: err, + } + return + } + + fmt.Printf("%#v\n", string(resp)) + + // FIXME: Replace this with above login call. sessionToken, err := extractSessionToken(claims) if err != nil { s.Result <- AuthorizationResult{ @@ -124,6 +165,7 @@ func (s *Server) handleCallback() http.HandlerFunc { } } +// AuthorizationResult represents the result of the authorization process. type AuthorizationResult struct { // Email address extracted from JWT claims. Email string diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index a724b5b7e..20a874e74 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -42,11 +42,14 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { } result := make(chan auth.AuthorizationResult) + endpoint, _ := c.Globals.Endpoint() s := auth.Server{ - Result: result, - Router: http.NewServeMux(), - Verifier: verifier, + HTTPClient: c.Globals.HTTPClient, + Result: result, + Router: http.NewServeMux(), + SessionEndpoint: endpoint, + Verifier: verifier, } s.Routes() From 53735b30b999f9f0778cc421d06405b62f4f8fa9 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 11:29:02 +0100 Subject: [PATCH 004/115] feat(undocumented): support debug output --- pkg/api/undocumented/undocumented.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index 4971d8d28..3ecb8044b 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -3,9 +3,11 @@ package undocumented import ( + "context" "fmt" "io" "net/http" + "net/http/httputil" "net/url" "strings" "time" @@ -50,6 +52,7 @@ type HTTPHeader struct { type CallOptions struct { APIEndpoint string Body io.Reader + Debug bool HTTPClient api.HTTPClient HTTPHeaders []HTTPHeader Method string @@ -75,6 +78,13 @@ func Call(opts CallOptions) (data []byte, err error) { req.Header.Set(header.Key, header.Value) } + if opts.Debug { + rc := req.Clone(context.Background()) + rc.Header.Set("Fastly-Key", "REDACTED") + dump, _ := httputil.DumpRequest(rc, true) + fmt.Printf("undocumented.Call request dump:\n\n%#v\n\n", string(dump)) + } + res, err := opts.HTTPClient.Do(req) if err != nil { if urlErr, ok := err.(*url.Error); ok && urlErr.Timeout() { @@ -87,6 +97,11 @@ func Call(opts CallOptions) (data []byte, err error) { } defer res.Body.Close() // #nosec G307 + if opts.Debug { + dump, _ := httputil.DumpResponse(res, true) + fmt.Printf("undocumented.Call request dump:\n\n%#v\n\n", string(dump)) + } + data, err = io.ReadAll(res.Body) if err != nil { return []byte{}, NewError(err, res.StatusCode) From 1bde0e0a474989e4c1cc1439e4fd1f3eecd37957 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 11:29:33 +0100 Subject: [PATCH 005/115] fix(app): avoid auth for config command --- pkg/app/run.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 22f03ab96..929c9077e 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -136,7 +136,7 @@ func Run(opts RunOpts) error { token, source := g.Token() - // FIXME: Identify token expiry! + // FIXME: Identify access AND refresh token expiry! // Ensure the user has configured an API token, otherwise trigger the // authentication flow (unless calling one of the profile commands). @@ -158,6 +158,8 @@ func Run(opts RunOpts) error { if err != nil { return fmt.Errorf("failed to authenticate: %w", err) } + text.Break(opts.Stdout) + break } } @@ -316,7 +318,7 @@ func commandCollectsData(command string) bool { // allowNoToken determines if the command to be executed is one that should work // even if there is no prior API token available. func allowNoToken(command string) bool { - if command == "version" || command == "whoami" || strings.HasPrefix(command, "profile ") { + if command == "version" || command == "config" || strings.HasPrefix(command, "profile ") { return true } return false From f828637aa325772691d703f47167aeee4fb7f312 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 11:30:26 +0100 Subject: [PATCH 006/115] fix: acquire api token --- pkg/auth/auth.go | 49 ++++++++++++++----------------- pkg/commands/authenticate/root.go | 12 ++++++-- pkg/config/config.go | 17 +++++++++-- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 6a3f3747b..252bed208 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -32,7 +32,7 @@ const ServerURL = "https://accounts.secretcdn-stg.net" const ClientID = "fastly-cli" // Audience is the unique identifier of the API your app wants to access. -// FIXME: Use --endpoint override (api.fastly.com) +// FIXME: Use --endpoint override (api.fastly.com) + allow env var override const Audience = "https://api.secretcdn-stg.net/" // RedirectURL is the endpoint the auth provider will pass an authorization code to. @@ -108,9 +108,6 @@ func (s *Server) handleCallback() http.HandlerFunc { return } - fmt.Printf("jwt: %+v\n\n", j) - fmt.Printf("claims: %+v\n\n", claims) - resp, err := undocumented.Call(undocumented.CallOptions{ APIEndpoint: s.SessionEndpoint, HTTPClient: s.HTTPClient, @@ -136,13 +133,11 @@ func (s *Server) handleCallback() http.HandlerFunc { return } - fmt.Printf("%#v\n", string(resp)) - - // FIXME: Replace this with above login call. - sessionToken, err := extractSessionToken(claims) + at := &APIToken{} + err = json.Unmarshal(resp, at) if err != nil { s.Result <- AuthorizationResult{ - Err: err, + Err: fmt.Errorf("failed to unmarshal json containing API token: %w", err), } return } @@ -150,7 +145,7 @@ func (s *Server) handleCallback() http.HandlerFunc { email, ok := claims["email"] if !ok { s.Result <- AuthorizationResult{ - Err: errors.New("unable to extract email from JWT claims"), + Err: errors.New("failed to extract email from JWT claims"), } return } @@ -160,11 +155,27 @@ func (s *Server) handleCallback() http.HandlerFunc { s.Result <- AuthorizationResult{ Email: email.(string), Jwt: j, - SessionToken: sessionToken, + SessionToken: at.AccessToken, } } } +// APIToken is returned from the /login-enhanced endpoint. +type APIToken struct { + // AccessToken is used to access the Fastly API. + AccessToken string `json:"access_token"` + // CustomerID is the customer ID. + CustomerID string `json:"customer_id"` + // ExpiresAt is when the access token will expire. + ExpiresAt string `json:"expires_at"` + // ID is a unique ID. + ID string `json:"id"` + // Name is a description of the token. + Name string `json:"name"` + // UserID is the user's ID. + UserID string `json:"user_id"` +} + // AuthorizationResult represents the result of the authorization process. type AuthorizationResult struct { // Email address extracted from JWT claims. @@ -230,16 +241,12 @@ func GetJWT(codeVerifier, authorizationCode string) (JWT, error) { return JWT{}, err } - fmt.Printf("body: %#v\n\n", string(body)) - var j JWT err = json.Unmarshal(body, &j) if err != nil { return JWT{}, err } - fmt.Printf("j: %#v\n\n", j) - return j, nil } @@ -282,18 +289,6 @@ func verifyJWTSignature(token string) (claims map[string]any, err error) { return claims, nil } -// extractSessionToken extracts a legacy session token from the given claims. -func extractSessionToken(claims map[string]any) (string, error) { - if i, ok := claims["legacy_session_token"]; ok { - if t, ok := i.(string); ok { - if t != "" { - return t, nil - } - } - } - return "", fmt.Errorf("failed to extract session token from JWT custom claim") -} - // refreshToken constructs and calls the token_endpoint for refreshing the // access token and returns the new access token. func refreshToken(token string) ([]byte, error) { diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 20a874e74..f3f8447db 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -96,11 +96,17 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { if profileName == "" { c.Globals.Config.Profiles[profile.DefaultName] = &config.Profile{ - Default: true, - Email: ar.Email, - Token: ar.SessionToken, + AccessToken: ar.Jwt.AccessToken, + AccessTokenTTL: ar.Jwt.ExpiresIn, + Default: true, + Email: ar.Email, + RefreshToken: ar.Jwt.RefreshToken, + RefreshTokenTTL: ar.Jwt.RefreshExpiresIn, + Token: ar.SessionToken, } } else { + // FIXME: The Edit() method needs to know about Access/Refresh settings. + // And also how to reauthenticate. ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, func(p *config.Profile) { p.Token = ar.SessionToken }) diff --git a/pkg/config/config.go b/pkg/config/config.go index 93af481b1..3c4b20bc9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -139,9 +139,20 @@ type Profiles map[string]*Profile // Profile represents a specific profile account. type Profile struct { - Default bool `toml:"default" json:"default"` - Email string `toml:"email" json:"email"` - Token string `toml:"token" json:"token"` + // AccessToken is used to acquire a new token when it expires. + AccessToken string `toml:"access_token" json:"access_token"` + // AccessTokenTTL indicates when the access token needs to be replaced. + AccessTokenTTL int `toml:"access_token_ttl" json:"access_token_ttl"` + // Default indicates if the profile is the default profile to use. + Default bool `toml:"default" json:"default"` + // Email is the email address associated with the token. + Email string `toml:"email" json:"email"` + // RefreshToken is used to acquire a new access token when it expires. + RefreshToken string `toml:"refresh_token" json:"refresh_token"` + // RefreshTokenTTL indicates when the refresh token needs to be replaced. + RefreshTokenTTL int `toml:"refresh_token_ttl" json:"refresh_token_ttl"` + // Token is a temporary token used to interact with the Fastly API. + Token string `toml:"token" json:"token"` } // StarterKitLanguages represents language specific starter kits. From e22ce5661335104ade9fc07c8bcee714a719283a Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 17:05:49 +0100 Subject: [PATCH 007/115] feat(auth): implement refresh behaviour --- pkg/app/run.go | 180 +++++++++++++++++++++--- pkg/auth/auth.go | 52 +++---- pkg/commands/authenticate/root.go | 52 ++++--- pkg/commands/configstoreentry/delete.go | 2 +- pkg/commands/kvstore/list.go | 2 +- pkg/commands/kvstoreentry/delete.go | 2 +- pkg/commands/secretstore/list.go | 2 +- pkg/commands/secretstoreentry/list.go | 5 +- pkg/config/config.go | 6 +- pkg/profile/profile.go | 7 +- 10 files changed, 244 insertions(+), 66 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 929c9077e..1a7394823 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -1,8 +1,11 @@ package app import ( + "encoding/json" + "errors" "fmt" "io" + "net/http" "os" "slices" "strconv" @@ -13,6 +16,8 @@ import ( "github.com/fastly/kingpin" "github.com/fastly/cli/pkg/api" + "github.com/fastly/cli/pkg/api/undocumented" + "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/commands/update" "github.com/fastly/cli/pkg/commands/version" "github.com/fastly/cli/pkg/config" @@ -134,18 +139,157 @@ func Run(opts RunOpts) error { opts.Manifest.File.SetQuiet(true) } - token, source := g.Token() + token, tokenSource := g.Token() - // FIXME: Identify access AND refresh token expiry! + authWarningMsg := "No API token could be found" + + // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. + if tokenSource == lookup.SourceFile && commandRequiresToken(commandName) { + var ( + data *config.Profile + found bool + name, profileName string + ) + switch { + case g.Flags.Profile != "": + profileName = g.Flags.Profile + case g.Manifest.File.Profile != "": + profileName = g.Manifest.File.Profile + default: + profileName = "default" + } + for name, data = range g.Config.Profiles { + if (profileName == "default" && data.Default) || name == profileName { + // Once we find the default profile we can update the variable to be the + // associated profile name so later on we can use that information to + // update the specific profile. + if profileName == "default" { + profileName = name + } + found = true + break + } + } + if !found { + return fmt.Errorf("failed to locate '%s' profile", profileName) + } + + ttl := time.Duration(data.AccessTokenTTL) * time.Second + diff := time.Now().Add(-ttl).Unix() + + // Access Token has expired + if data.AccessTokenCreated < diff { + ttl := time.Duration(data.RefreshTokenTTL) * time.Second + diff := time.Now().Add(-ttl).Unix() + + if data.RefreshTokenCreated < diff { + authWarningMsg = "Your API token has expired and so has your refresh token" + // re-authenticate we simple unset the tokenSource + // the following conditional block will catch it and trigger a re-auth. + tokenSource = lookup.SourceUndefined + } else { + if !g.Flags.Quiet { + text.Info(opts.Stdout, "Your access token has now expired. We will attempt to refresh it") + } + text.Break(opts.Stdout) + j, err := auth.RefreshAccessToken(data.RefreshToken) + if err != nil { + return fmt.Errorf("failed to refresh access token: %w", err) + } + + claims, err := auth.VerifyJWTSignature(j.AccessToken) + if err != nil { + return fmt.Errorf("failed to verify refreshed JWT: %w", err) + } + + email, ok := claims["email"] + if !ok { + return errors.New("failed to extract email from JWT claims") + } + + endpoint, _ := g.Endpoint() + + // Exchange the access token for an API token + resp, err := undocumented.Call(undocumented.CallOptions{ + APIEndpoint: endpoint, + HTTPClient: g.HTTPClient, + HTTPHeaders: []undocumented.HTTPHeader{ + { + Key: "Authorization", + Value: fmt.Sprintf("Bearer %s", j.AccessToken), + }, + }, + Method: http.MethodPost, + Path: "/login-enhanced", + }) + if err != nil { + if apiErr, ok := err.(undocumented.APIError); ok { + if apiErr.StatusCode != http.StatusConflict { + err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) + } + } + return err + } + + at := &auth.APIToken{} + err = json.Unmarshal(resp, at) + if err != nil { + return fmt.Errorf("failed to unmarshal json containing API token: %w", err) + } + + // NOTE: The refresh token can be updated along with the access token. + // This happens all the time in my testing but according to what is + // spec'd this apparently is something that _might_ happen. + name, p := profile.Get(profileName, g.Config.Profiles) + if name == "" { + return fmt.Errorf("failed to locate '%s' profile", profileName) + } + now := time.Now().Unix() + refreshToken := p.RefreshToken + refreshTokenCreated := p.RefreshTokenCreated + refreshTokenTTL := p.RefreshTokenTTL + if p.RefreshToken != j.RefreshToken { + if !g.Flags.Quiet { + text.Info(opts.Stdout, "Your refresh token was also updated") + } + refreshToken = j.RefreshToken + refreshTokenCreated = now + refreshTokenTTL = j.RefreshExpiresIn + } + + ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { + p.AccessToken = j.AccessToken + p.AccessTokenCreated = now + p.AccessTokenTTL = j.ExpiresIn + p.Email = email.(string) + p.RefreshToken = refreshToken + p.RefreshTokenCreated = refreshTokenCreated + p.RefreshTokenTTL = refreshTokenTTL + p.Token = at.AccessToken + }) + if !ok { + return fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update default profile with new token data"), + Remediation: "Run `fastly authenticate` to retry.", + } + } + g.Config.Profiles = ps + if err := g.Config.Write(g.ConfigPath); err != nil { + g.ErrLog.Add(err) + return fmt.Errorf("error saving config file: %w", err) + } + } + } + } // Ensure the user has configured an API token, otherwise trigger the // authentication flow (unless calling one of the profile commands). - if source == lookup.SourceUndefined && !allowNoToken(commandName) { + if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { for _, command := range commands { if command.Name() == "authenticate" { - text.Warning(opts.Stdout, "No API token could be found. We need to open your browser to authenticate you.") + text.Warning(opts.Stdout, "%s. We need to open your browser to authenticate you.", authWarningMsg) text.Break(opts.Stdout) - cont, err := text.AskYesNo(opts.Stdout, "Are you sure you want to continue? [yes/no]: ", opts.Stdin) + cont, err := text.AskYesNo(opts.Stdout, "Are you sure you want to continue? [y/N]: ", opts.Stdin) if err != nil { return err } @@ -165,15 +309,15 @@ func Run(opts RunOpts) error { } // Recheck for token (should be persisted to profile data). - token, source = g.Token() - if source == lookup.SourceUndefined { + token, tokenSource = g.Token() + if tokenSource == lookup.SourceUndefined { return fsterr.ErrNoToken } } if g.Verbose() { displayTokenSource( - source, + tokenSource, opts.Stdout, env.Token, determineProfile(opts.Manifest.File.Profile, g.Flags.Profile, g.Config.Profiles), @@ -189,7 +333,7 @@ func Run(opts RunOpts) error { // to assert if they are not too open or have been altered outside of the // application and warn if so. segs := strings.Split(commandName, " ") - if source == lookup.SourceFile && (len(segs) > 0 && segs[0] != "profile") { + if tokenSource == lookup.SourceFile && (len(segs) > 0 && segs[0] != "profile") { if fi, err := os.Stat(config.FilePath); err == nil { if mode := fi.Mode().Perm(); mode > config.FilePermissions { if !g.Flags.Quiet { @@ -201,9 +345,9 @@ func Run(opts RunOpts) error { } } - endpoint, source := g.Endpoint() + endpoint, endpointSource := g.Endpoint() if g.Verbose() { - switch source { + switch endpointSource { case lookup.SourceEnvironment: fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) case lookup.SourceFile: @@ -315,11 +459,13 @@ func commandCollectsData(command string) bool { return false } -// allowNoToken determines if the command to be executed is one that should work -// even if there is no prior API token available. -func allowNoToken(command string) bool { - if command == "version" || command == "config" || strings.HasPrefix(command, "profile ") { - return true +// commandRequiresToken determines if the command to be executed is one that +// requires an API token. +func commandRequiresToken(command string) bool { + command = strings.Split(command, " ")[0] + switch command { + case "authenticate", "config", "profile", "update", "version": + return false } - return false + return true } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 252bed208..275d4cd4a 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -100,7 +100,7 @@ func (s *Server) handleCallback() http.HandlerFunc { return } - claims, err := verifyJWTSignature(j.AccessToken) + claims, err := VerifyJWTSignature(j.AccessToken) if err != nil { s.Result <- AuthorizationResult{ Err: err, @@ -108,6 +108,15 @@ func (s *Server) handleCallback() http.HandlerFunc { return } + email, ok := claims["email"] + if !ok { + s.Result <- AuthorizationResult{ + Err: errors.New("failed to extract email from JWT claims"), + } + return + } + + // Exchange the access token for an API token resp, err := undocumented.Call(undocumented.CallOptions{ APIEndpoint: s.SessionEndpoint, HTTPClient: s.HTTPClient, @@ -121,7 +130,6 @@ func (s *Server) handleCallback() http.HandlerFunc { Path: "/login-enhanced", }) if err != nil { - fmt.Printf("error response from undocumented call: %#v\n", string(resp)) if apiErr, ok := err.(undocumented.APIError); ok { if apiErr.StatusCode != http.StatusConflict { err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) @@ -142,14 +150,6 @@ func (s *Server) handleCallback() http.HandlerFunc { return } - email, ok := claims["email"] - if !ok { - s.Result <- AuthorizationResult{ - Err: errors.New("failed to extract email from JWT claims"), - } - return - } - fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") s.Result <- AuthorizationResult{ @@ -269,8 +269,8 @@ type JWT struct { TokenType string `json:"token_type"` } -// verifyJWTSignature calls the jwks_uri endpoint and extracts its claims. -func verifyJWTSignature(token string) (claims map[string]any, err error) { +// VerifyJWTSignature calls the jwks_uri endpoint and extracts its claims. +func VerifyJWTSignature(token string) (claims map[string]any, err error) { ctx := context.Background() path := "/realms/fastly/protocol/openid-connect/certs" @@ -289,40 +289,44 @@ func verifyJWTSignature(token string) (claims map[string]any, err error) { return claims, nil } -// refreshToken constructs and calls the token_endpoint for refreshing the -// access token and returns the new access token. -func refreshToken(token string) ([]byte, error) { +// RefreshAccessToken constructs and calls the token_endpoint with the +// refresh token so we can refresh and return the access token. +func RefreshAccessToken(refreshToken string) (JWT, error) { path := "/realms/fastly/protocol/openid-connect/token" payload := fmt.Sprintf( "grant_type=refresh_token&client_id=%s&refresh_token=%s", ClientID, - token, + refreshToken, ) req, err := http.NewRequest("POST", ServerURL+path, strings.NewReader(payload)) if err != nil { - return nil, err + return JWT{}, err } req.Header.Add("content-type", "application/x-www-form-urlencoded") res, err := http.DefaultClient.Do(req) if err != nil { - return nil, err + return JWT{}, err } defer res.Body.Close() + body, err := io.ReadAll(res.Body) + if err != nil { + return JWT{}, err + } + if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to refresh the access token (status: %s)", res.Status) + return JWT{}, fmt.Errorf("failed to refresh the access token (status: %s)", res.Status) } - body, err := io.ReadAll(res.Body) + var j JWT + err = json.Unmarshal(body, &j) if err != nil { - return nil, err + return JWT{}, err } - fmt.Printf("refresh body: %#v\n\n", string(body)) - - return body, nil + return j, nil } diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index f3f8447db..94d6e7a37 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "time" "github.com/hashicorp/cap/oidc" "github.com/skratchdot/open-golang/open" @@ -92,28 +93,50 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { } } - profileName, _ := profile.Default(c.Globals.Config.Profiles) + var profileConfigured string + switch { + case c.Globals.Flags.Profile != "": + profileConfigured = c.Globals.Flags.Profile + case c.Globals.Manifest.File.Profile != "": + profileConfigured = c.Globals.Manifest.File.Profile + } + + profileDefault, _ := profile.Default(c.Globals.Config.Profiles) - if profileName == "" { + // If no profiles configured at all... + if profileConfigured == "" && profileDefault == "" { + now := time.Now().Unix() c.Globals.Config.Profiles[profile.DefaultName] = &config.Profile{ - AccessToken: ar.Jwt.AccessToken, - AccessTokenTTL: ar.Jwt.ExpiresIn, - Default: true, - Email: ar.Email, - RefreshToken: ar.Jwt.RefreshToken, - RefreshTokenTTL: ar.Jwt.RefreshExpiresIn, - Token: ar.SessionToken, + AccessToken: ar.Jwt.AccessToken, + AccessTokenCreated: now, + AccessTokenTTL: ar.Jwt.ExpiresIn, + Default: true, + Email: ar.Email, + RefreshToken: ar.Jwt.RefreshToken, + RefreshTokenCreated: now, + RefreshTokenTTL: ar.Jwt.RefreshExpiresIn, + Token: ar.SessionToken, } } else { - // FIXME: The Edit() method needs to know about Access/Refresh settings. - // And also how to reauthenticate. + profileName := profileDefault + if profileConfigured != "" { + profileName = profileConfigured + } ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, func(p *config.Profile) { + now := time.Now().Unix() + p.AccessToken = ar.Jwt.AccessToken + p.AccessTokenCreated = now + p.AccessTokenTTL = ar.Jwt.ExpiresIn + p.Email = ar.Email + p.RefreshToken = ar.Jwt.RefreshToken + p.RefreshTokenCreated = now + p.RefreshTokenTTL = ar.Jwt.RefreshExpiresIn p.Token = ar.SessionToken }) if !ok { return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update default profile with new session token"), - Remediation: "Run `fastly profile update` and manually paste in the session token.", + Inner: fmt.Errorf("failed to update default profile with new token data"), + Remediation: "Run `fastly authenticate` to retry.", } } c.Globals.Config.Profiles = ps @@ -124,9 +147,6 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { return fmt.Errorf("error saving config file: %w", err) } - // FIXME: Don't just update the default profile. - // Allow user to configure this via a --profile flag. - text.Success(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) return nil } diff --git a/pkg/commands/configstoreentry/delete.go b/pkg/commands/configstoreentry/delete.go index 97f4b2323..8065c3ce9 100644 --- a/pkg/commands/configstoreentry/delete.go +++ b/pkg/commands/configstoreentry/delete.go @@ -83,7 +83,7 @@ func (c *DeleteCommand) Exec(in io.Reader, out io.Writer) error { if c.deleteAll { if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { text.Warning(out, "This will delete ALL entries from your store!\n\n") - cont, err := text.AskYesNo(out, "Are you sure you want to continue? [yes/no]: ", in) + cont, err := text.AskYesNo(out, "Are you sure you want to continue? [y/N]: ", in) if err != nil { return err } diff --git a/pkg/commands/kvstore/list.go b/pkg/commands/kvstore/list.go index 866be738f..659ece4eb 100644 --- a/pkg/commands/kvstore/list.go +++ b/pkg/commands/kvstore/list.go @@ -69,7 +69,7 @@ func (c *ListCommand) Exec(in io.Reader, out io.Writer) error { // Check if 'out' is interactive before prompting. if !c.Globals.Flags.NonInteractive && !c.Globals.Flags.AutoYes && text.IsTTY(out) { text.Break(out) - printNext, err := text.AskYesNo(out, "Print next page [yes/no]: ", in) + printNext, err := text.AskYesNo(out, "Print next page [y/N]: ", in) if err != nil { return err } diff --git a/pkg/commands/kvstoreentry/delete.go b/pkg/commands/kvstoreentry/delete.go index 7ddb165cc..a8f5355e6 100644 --- a/pkg/commands/kvstoreentry/delete.go +++ b/pkg/commands/kvstoreentry/delete.go @@ -73,7 +73,7 @@ func (c *DeleteCommand) Exec(in io.Reader, out io.Writer) error { if c.deleteAll { if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { text.Warning(out, "This will delete ALL entries from your store!\n\n") - cont, err := text.AskYesNo(out, "Are you sure you want to continue? [yes/no]: ", in) + cont, err := text.AskYesNo(out, "Are you sure you want to continue? [y/N]: ", in) if err != nil { return err } diff --git a/pkg/commands/secretstore/list.go b/pkg/commands/secretstore/list.go index 0c799c166..7411c3cb8 100644 --- a/pkg/commands/secretstore/list.go +++ b/pkg/commands/secretstore/list.go @@ -71,7 +71,7 @@ func (c *ListCommand) Exec(in io.Reader, out io.Writer) error { if o.Meta.NextCursor != "" { text.Break(out) - printNext, err := text.AskYesNo(out, "Print next page [yes/no]: ", in) + printNext, err := text.AskYesNo(out, "Print next page [y/N]: ", in) if err != nil { return err } diff --git a/pkg/commands/secretstoreentry/list.go b/pkg/commands/secretstoreentry/list.go index 87c361647..ad8fef6c5 100644 --- a/pkg/commands/secretstoreentry/list.go +++ b/pkg/commands/secretstoreentry/list.go @@ -3,12 +3,13 @@ package secretstoreentry import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. @@ -65,7 +66,7 @@ func (c *ListCommand) Exec(in io.Reader, out io.Writer) error { if o != nil && o.Meta.NextCursor != "" { // Check if 'out' is interactive before prompting. if !c.Globals.Flags.NonInteractive && !c.Globals.Flags.AutoYes && text.IsTTY(out) { - printNext, err := text.AskYesNo(out, "Print next page [yes/no]: ", in) + printNext, err := text.AskYesNo(out, "Print next page [y/N]: ", in) if err != nil { return err } diff --git a/pkg/config/config.go b/pkg/config/config.go index 3c4b20bc9..bf3d2c051 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -139,8 +139,10 @@ type Profiles map[string]*Profile // Profile represents a specific profile account. type Profile struct { - // AccessToken is used to acquire a new token when it expires. + // AccessToken is used to acquire an API token. AccessToken string `toml:"access_token" json:"access_token"` + // AccessTokenCreated indicates when the access token was created. + AccessTokenCreated int64 `toml:"access_token_created" json:"access_token_created"` // AccessTokenTTL indicates when the access token needs to be replaced. AccessTokenTTL int `toml:"access_token_ttl" json:"access_token_ttl"` // Default indicates if the profile is the default profile to use. @@ -149,6 +151,8 @@ type Profile struct { Email string `toml:"email" json:"email"` // RefreshToken is used to acquire a new access token when it expires. RefreshToken string `toml:"refresh_token" json:"refresh_token"` + // RefreshTokenCreated indicates when the refresh token was created. + RefreshTokenCreated int64 `toml:"refresh_token_created" json:"refresh_token_created"` // RefreshTokenTTL indicates when the refresh token needs to be replaced. RefreshTokenTTL int `toml:"refresh_token_ttl" json:"refresh_token_ttl"` // Token is a temporary token used to interact with the Fastly API. diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index 7946ae7d6..e9d185f63 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -85,8 +85,11 @@ type EditOption func(*config.Profile) // Edit modifies the named profile. // -// NOTE: The type assigned to the config.Profiles map key value is a struct. -// Structs are passed by value and so we must return the mutated type. +// IMPORTANT: We must return config.Profiles to safely update in-memory data. +// The type assigned to the config.Profiles map key value is a struct and +// structs are passed by value, so we must return the mutated type so the +// caller so they can reassign the updated struct back to the in-memory data +// and then persist that data back to disk. func Edit(name string, p config.Profiles, opts ...EditOption) (config.Profiles, bool) { var ok bool for k, v := range p { From 1659e40622e3d1b9a6ebe08b48f03b56ef672f5c Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 19:14:26 +0100 Subject: [PATCH 008/115] refactor: move FastlyAPIClient out of app package --- pkg/app/run.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 1a7394823..1ea8b65f7 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -32,16 +32,6 @@ import ( "github.com/fastly/cli/pkg/text" ) -// FastlyAPIClient is a ClientFactory that returns a real Fastly API client -// using the provided token and endpoint. -func FastlyAPIClient(token, endpoint string, debugMode bool) (api.Interface, error) { - client, err := fastly.NewClientForEndpoint(token, endpoint) - if debugMode { - client.DebugMode = true - } - return client, err -} - // Run constructs the application including all of the subcommands, parses the // args, invokes the client factory with the token to create a Fastly API // client, and executes the chosen command, using the provided io.Reader and @@ -175,17 +165,17 @@ func Run(opts RunOpts) error { } ttl := time.Duration(data.AccessTokenTTL) * time.Second - diff := time.Now().Add(-ttl).Unix() + delta := time.Now().Add(-ttl).Unix() // Access Token has expired - if data.AccessTokenCreated < diff { + if data.AccessTokenCreated < delta { ttl := time.Duration(data.RefreshTokenTTL) * time.Second diff := time.Now().Add(-ttl).Unix() if data.RefreshTokenCreated < diff { authWarningMsg = "Your API token has expired and so has your refresh token" - // re-authenticate we simple unset the tokenSource - // the following conditional block will catch it and trigger a re-auth. + // To re-authenticate we simple reset the tokenSource variable. + // A later conditional block catches it and trigger a re-auth. tokenSource = lookup.SourceUndefined } else { if !g.Flags.Quiet { From 2549adc819fce75c38616294b5ad247c9fc52a36 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 19:22:14 +0100 Subject: [PATCH 009/115] refactor: variable naming --- pkg/app/run.go | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 1ea8b65f7..c68af90bd 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -136,7 +136,7 @@ func Run(opts RunOpts) error { // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. if tokenSource == lookup.SourceFile && commandRequiresToken(commandName) { var ( - data *config.Profile + profileData *config.Profile found bool name, profileName string ) @@ -148,8 +148,8 @@ func Run(opts RunOpts) error { default: profileName = "default" } - for name, data = range g.Config.Profiles { - if (profileName == "default" && data.Default) || name == profileName { + for name, profileData = range g.Config.Profiles { + if (profileName == "default" && profileData.Default) || name == profileName { // Once we find the default profile we can update the variable to be the // associated profile name so later on we can use that information to // update the specific profile. @@ -164,15 +164,15 @@ func Run(opts RunOpts) error { return fmt.Errorf("failed to locate '%s' profile", profileName) } - ttl := time.Duration(data.AccessTokenTTL) * time.Second + ttl := time.Duration(profileData.AccessTokenTTL) * time.Second delta := time.Now().Add(-ttl).Unix() // Access Token has expired - if data.AccessTokenCreated < delta { - ttl := time.Duration(data.RefreshTokenTTL) * time.Second + if profileData.AccessTokenCreated < delta { + ttl := time.Duration(profileData.RefreshTokenTTL) * time.Second diff := time.Now().Add(-ttl).Unix() - if data.RefreshTokenCreated < diff { + if profileData.RefreshTokenCreated < diff { authWarningMsg = "Your API token has expired and so has your refresh token" // To re-authenticate we simple reset the tokenSource variable. // A later conditional block catches it and trigger a re-auth. @@ -182,12 +182,12 @@ func Run(opts RunOpts) error { text.Info(opts.Stdout, "Your access token has now expired. We will attempt to refresh it") } text.Break(opts.Stdout) - j, err := auth.RefreshAccessToken(data.RefreshToken) + updated, err := auth.RefreshAccessToken(profileData.RefreshToken) if err != nil { return fmt.Errorf("failed to refresh access token: %w", err) } - claims, err := auth.VerifyJWTSignature(j.AccessToken) + claims, err := auth.VerifyJWTSignature(updated.AccessToken) if err != nil { return fmt.Errorf("failed to verify refreshed JWT: %w", err) } @@ -206,7 +206,7 @@ func Run(opts RunOpts) error { HTTPHeaders: []undocumented.HTTPHeader{ { Key: "Authorization", - Value: fmt.Sprintf("Bearer %s", j.AccessToken), + Value: fmt.Sprintf("Bearer %s", updated.AccessToken), }, }, Method: http.MethodPost, @@ -227,30 +227,33 @@ func Run(opts RunOpts) error { return fmt.Errorf("failed to unmarshal json containing API token: %w", err) } - // NOTE: The refresh token can be updated along with the access token. + // NOTE: The refresh token can sometimes be refreshed along with the access token. // This happens all the time in my testing but according to what is // spec'd this apparently is something that _might_ happen. - name, p := profile.Get(profileName, g.Config.Profiles) + // So after we get the refreshed access token, we check to see if the + // refresh token that was returned by the API call has also changed when + // compared to the refresh token stored in the CLI config file. + name, current := profile.Get(profileName, g.Config.Profiles) if name == "" { return fmt.Errorf("failed to locate '%s' profile", profileName) } now := time.Now().Unix() - refreshToken := p.RefreshToken - refreshTokenCreated := p.RefreshTokenCreated - refreshTokenTTL := p.RefreshTokenTTL - if p.RefreshToken != j.RefreshToken { + refreshToken := current.RefreshToken + refreshTokenCreated := current.RefreshTokenCreated + refreshTokenTTL := current.RefreshTokenTTL + if current.RefreshToken != updated.RefreshToken { if !g.Flags.Quiet { text.Info(opts.Stdout, "Your refresh token was also updated") } - refreshToken = j.RefreshToken + refreshToken = updated.RefreshToken refreshTokenCreated = now - refreshTokenTTL = j.RefreshExpiresIn + refreshTokenTTL = updated.RefreshExpiresIn } ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { - p.AccessToken = j.AccessToken + p.AccessToken = updated.AccessToken p.AccessTokenCreated = now - p.AccessTokenTTL = j.ExpiresIn + p.AccessTokenTTL = updated.ExpiresIn p.Email = email.(string) p.RefreshToken = refreshToken p.RefreshTokenCreated = refreshTokenCreated From b6972c0493cb340e51403cadeeb57e1f1da4ddd4 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 19:30:10 +0100 Subject: [PATCH 010/115] refactor: display profile name --- pkg/app/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index c68af90bd..b53f1d8e5 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -262,7 +262,7 @@ func Run(opts RunOpts) error { }) if !ok { return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update default profile with new token data"), + Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), Remediation: "Run `fastly authenticate` to retry.", } } From 0db7fcfa1e5e7e9083a40be4a22aa816597fc792 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 5 Sep 2023 19:31:55 +0100 Subject: [PATCH 011/115] refactor: clarify tokenless commands --- pkg/app/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index b53f1d8e5..d3a2317cf 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -276,7 +276,7 @@ func Run(opts RunOpts) error { } // Ensure the user has configured an API token, otherwise trigger the - // authentication flow (unless calling one of the profile commands). + // authentication flow (unless calling a tokenless command). if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { for _, command := range commands { if command.Name() == "authenticate" { From 4b77ceb5f984f21620125286b94715f28f0dfb65 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 6 Sep 2023 14:22:04 +0100 Subject: [PATCH 012/115] doc(testing): explain delve debugging --- TESTING.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/TESTING.md b/TESTING.md index 37f92fad8..907707432 100644 --- a/TESTING.md +++ b/TESTING.md @@ -52,3 +52,21 @@ TEST_COMPUTE_INIT=1 TEST_COMPUTE_BUILD=1 TEST_COMPUTE_DEPLOY=1 TEST_COMMAND=gote ``` > **NOTE**: `TEST_COMMAND` is optional and allows the use of https://github.com/rakyll/gotest to improve test output. + +### Debugging + +To debug failing tests you can use [Delve](<>). + +Essentially, `cd` into a package directory (where the `_test.go` file is you want to run) and then execute... + +``` +TEST_COMPUTE_BUILD=1 dlv test -- -test.v -test.run TestNameGoesHere +``` + +Once that is done, you can set breakpoints. For example: + +``` +break ../../app/run.go:152 +``` + +> **NOTE:** The path is relative to the package directory you're running the test file. From 97d8d51bbf24a15e9ddcecb48d37a99160d4588e Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 6 Sep 2023 14:22:26 +0100 Subject: [PATCH 013/115] tests: fix --- .tmpl/create.go | 5 -- .tmpl/delete.go | 5 -- .tmpl/describe.go | 5 -- .tmpl/list.go | 5 -- .tmpl/update.go | 5 -- pkg/app/run.go | 3 + pkg/commands/acl/acl_test.go | 3 +- pkg/commands/aclentry/aclentry_test.go | 2 +- pkg/commands/authtoken/authtoken_test.go | 37 ++--------- pkg/commands/authtoken/create.go | 12 +--- pkg/commands/authtoken/delete.go | 7 -- pkg/commands/authtoken/describe.go | 8 +-- pkg/commands/authtoken/list.go | 5 -- pkg/commands/backend/backend_test.go | 2 +- pkg/commands/compute/build_test.go | 29 ++++++--- pkg/commands/compute/deploy_test.go | 5 -- pkg/commands/compute/update.go | 6 -- pkg/commands/compute/update_test.go | 4 +- pkg/commands/dictionary/dictionary_test.go | 4 +- pkg/commands/domain/domain_test.go | 22 +++---- pkg/commands/domain/validate.go | 9 +-- pkg/commands/healthcheck/healthcheck_test.go | 2 +- pkg/commands/ip/ip_test.go | 2 +- pkg/commands/ip/root.go | 7 -- .../azureblob/azureblob_integration_test.go | 2 +- .../bigquery/bigquery_integration_test.go | 2 +- .../cloudfiles/cloudfiles_integration_test.go | 5 +- .../datadog/datadog_integration_test.go | 2 +- .../digitalocean_integration_test.go | 2 +- .../elasticsearch_integration_test.go | 2 +- .../logging/ftp/ftp_integration_test.go | 2 +- .../logging/gcs/gcs_integration_test.go | 2 +- .../googlepubsub_integration_test.go | 2 +- .../logging/heroku/heroku_integration_test.go | 5 +- .../honeycomb/honeycomb_integration_test.go | 2 +- .../logging/https/https_integration_test.go | 2 +- .../logging/kafka/kafka_integration_test.go | 7 +- .../kinesis/kinesis_integration_test.go | 5 +- .../logging/loggly/loggly_integration_test.go | 2 +- .../logshuttle/logshuttle_integration_test.go | 2 +- .../logging/newrelic/newrelic_test.go | 5 +- .../logging/newrelicotlp/newrelicotlp_test.go | 5 +- .../openstack/openstack_integration_test.go | 2 +- .../papertrail/papertrail_integration_test.go | 2 +- .../logging/s3/s3_integration_test.go | 2 +- .../logging/scalyr/scalyr_integration_test.go | 2 +- .../logging/sftp/sftp_integration_test.go | 2 +- .../logging/splunk/splunk_integration_test.go | 5 +- .../sumologic/sumologic_integration_test.go | 2 +- .../logging/syslog/syslog_integration_test.go | 2 +- pkg/commands/pop/pop_test.go | 5 +- pkg/commands/pop/root.go | 7 -- pkg/commands/profile/profile_test.go | 12 ++++ pkg/commands/purge/purge_test.go | 52 +++++---------- pkg/commands/purge/root.go | 6 -- pkg/commands/ratelimit/create.go | 9 +-- pkg/commands/ratelimit/delete.go | 7 -- pkg/commands/ratelimit/describe.go | 6 -- pkg/commands/ratelimit/list.go | 9 +-- pkg/commands/ratelimit/ratelimit_test.go | 20 +++--- pkg/commands/ratelimit/update.go | 5 -- pkg/commands/service/service_test.go | 6 +- pkg/commands/serviceauth/service_test.go | 2 +- .../serviceversion/serviceversion_test.go | 2 +- .../custom/certificate/certificate_test.go | 5 +- pkg/commands/user/create.go | 10 +-- pkg/commands/user/delete.go | 10 +-- pkg/commands/user/describe.go | 9 +-- pkg/commands/user/list.go | 8 +-- pkg/commands/user/update.go | 10 +-- pkg/commands/user/user_test.go | 65 +++++++------------ pkg/commands/vcl/custom/custom_test.go | 5 +- pkg/commands/vcl/snippet/snippet_test.go | 2 +- pkg/commands/whoami/root.go | 8 +-- pkg/commands/whoami/whoami_test.go | 22 +++---- pkg/config/config_test.go | 13 +++- pkg/testutil/args.go | 26 +++++++- 77 files changed, 231 insertions(+), 379 deletions(-) diff --git a/.tmpl/create.go b/.tmpl/create.go index 6f44261e6..c91d5baf1 100644 --- a/.tmpl/create.go +++ b/.tmpl/create.go @@ -61,11 +61,6 @@ type CreateCommand struct { // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(in io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == config.SourceUndefined { - return errors.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, Client: c.Globals.Client, diff --git a/.tmpl/delete.go b/.tmpl/delete.go index 2cd91ec05..cbb391817 100644 --- a/.tmpl/delete.go +++ b/.tmpl/delete.go @@ -61,11 +61,6 @@ type DeleteCommand struct { // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(in io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == config.SourceUndefined { - return errors.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, Client: c.Globals.Client, diff --git a/.tmpl/describe.go b/.tmpl/describe.go index 07abd0d72..9c43299c9 100644 --- a/.tmpl/describe.go +++ b/.tmpl/describe.go @@ -56,11 +56,6 @@ type DescribeCommand struct { // Exec invokes the application logic for the command. func (c *DescribeCommand) Exec(in io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == config.SourceUndefined { - return errors.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, Client: c.Globals.Client, diff --git a/.tmpl/list.go b/.tmpl/list.go index fbaf51f1d..8753205c7 100644 --- a/.tmpl/list.go +++ b/.tmpl/list.go @@ -57,11 +57,6 @@ type ListCommand struct { // Exec invokes the application logic for the command. func (c *ListCommand) Exec(in io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == config.SourceUndefined { - return errors.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, Client: c.Globals.Client, diff --git a/.tmpl/update.go b/.tmpl/update.go index 2bfed0ea9..6f713f03a 100644 --- a/.tmpl/update.go +++ b/.tmpl/update.go @@ -65,11 +65,6 @@ type UpdateCommand struct { // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == config.SourceUndefined { - return errors.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, Client: c.Globals.Client, diff --git a/pkg/app/run.go b/pkg/app/run.go index d3a2317cf..8a1f56013 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -455,6 +455,9 @@ func commandCollectsData(command string) bool { // commandRequiresToken determines if the command to be executed is one that // requires an API token. func commandRequiresToken(command string) bool { + if command == "compute init" { + return false + } command = strings.Split(command, " ")[0] switch command { case "authenticate", "config", "profile", "update", "version": diff --git a/pkg/commands/acl/acl_test.go b/pkg/commands/acl/acl_test.go index 1d4a2828e..0ccdc23a9 100644 --- a/pkg/commands/acl/acl_test.go +++ b/pkg/commands/acl/acl_test.go @@ -90,6 +90,7 @@ func TestACLCreate(t *testing.T) { opts := testutil.NewRunOpts(testcase.Args, &stdout) opts.APIClient = mock.APIClient(testcase.API) err := app.Run(opts) + t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -282,7 +283,7 @@ func TestACLList(t *testing.T) { ListACLsFn: listACLs, }, Args: args("acl list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: 456\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: 789\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\n", + WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: 456\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: 789\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\n", }, } diff --git a/pkg/commands/aclentry/aclentry_test.go b/pkg/commands/aclentry/aclentry_test.go index 297a42b4a..839189957 100644 --- a/pkg/commands/aclentry/aclentry_test.go +++ b/pkg/commands/aclentry/aclentry_test.go @@ -348,7 +348,7 @@ var listACLEntriesOutputPageTwo = `SERVICE ID ID IP SUBNET NEGATED 123 789 127.0.0.2 0 true ` -var listACLEntriesOutputVerbose = `Fastly API token not provided +var listACLEntriesOutputVerbose = `Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/authtoken/authtoken_test.go b/pkg/commands/authtoken/authtoken_test.go index 14e57dfbc..f328c6a7a 100644 --- a/pkg/commands/authtoken/authtoken_test.go +++ b/pkg/commands/authtoken/authtoken_test.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -22,11 +21,6 @@ func TestCreate(t *testing.T) { Args: args("auth-token create"), WantError: "error parsing arguments: required flag --password not provided", }, - { - Name: "validate missing --token flag", - Args: args("auth-token create --password secure"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate CreateToken API error", API: mock.API{ @@ -87,11 +81,6 @@ func TestCreate(t *testing.T) { func TestDelete(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing --token flag", - Args: args("auth-token delete"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate missing optional flags", Args: args("auth-token delete --token 123"), @@ -185,11 +174,6 @@ func TestDelete(t *testing.T) { func TestDescribe(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing --token flag", - Args: args("auth-token describe"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate GetTokenSelf API error", API: mock.API{ @@ -230,13 +214,6 @@ func TestList(t *testing.T) { SetEnv bool } scenarios := []ts{ - { - TestScenario: testutil.TestScenario{ - Name: "validate missing --token flag", - Args: args("auth-token list"), - WantError: errors.ErrNoToken.Inner.Error(), - }, - }, { TestScenario: testutil.TestScenario{ Name: "validate ListTokens API error", @@ -245,7 +222,7 @@ func TestList(t *testing.T) { return nil, testutil.Err }, }, - Args: args("auth-token list --token 123"), + Args: args("auth-token list"), WantError: testutil.Err.Error(), }, }, @@ -257,7 +234,7 @@ func TestList(t *testing.T) { return nil, testutil.Err }, }, - Args: args("auth-token list --customer-id 123 --token 123"), + Args: args("auth-token list --customer-id 123"), WantError: testutil.Err.Error(), }, }, @@ -267,7 +244,7 @@ func TestList(t *testing.T) { API: mock.API{ ListTokensFn: listTokens, }, - Args: args("auth-token list --token 123"), + Args: args("auth-token list"), WantOutput: listTokenOutputSummary(false), }, }, @@ -277,7 +254,7 @@ func TestList(t *testing.T) { API: mock.API{ ListCustomerTokensFn: listCustomerTokens, }, - Args: args("auth-token list --customer-id 123 --token 123"), + Args: args("auth-token list --customer-id 123"), WantOutput: listTokenOutputSummary(false), }, }, @@ -287,7 +264,7 @@ func TestList(t *testing.T) { API: mock.API{ ListCustomerTokensFn: listCustomerTokens, }, - Args: args("auth-token list --token 123"), + Args: args("auth-token list"), WantOutput: listTokenOutputSummary(true), }, SetEnv: true, @@ -298,7 +275,7 @@ func TestList(t *testing.T) { API: mock.API{ ListTokensFn: listTokens, }, - Args: args("auth-token list --token 123 --verbose"), + Args: args("auth-token list --verbose"), WantOutput: listTokenOutputVerbose(), }, }, @@ -391,7 +368,7 @@ Expires at: 2021-06-15 23:00:00 +0000 UTC` } func listTokenOutputVerbose() string { - return `Fastly API token provided via --token + return `Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com diff --git a/pkg/commands/authtoken/create.go b/pkg/commands/authtoken/create.go index 4a509d12e..7c9992758 100644 --- a/pkg/commands/authtoken/create.go +++ b/pkg/commands/authtoken/create.go @@ -5,14 +5,13 @@ import ( "strings" "time" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/kingpin" + "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" - "github.com/fastly/kingpin" ) // Scopes is a list of purging scope options. @@ -65,11 +64,6 @@ type CreateCommand struct { // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - input := c.constructInput() r, err := c.Globals.APIClient.CreateToken(input) diff --git a/pkg/commands/authtoken/delete.go b/pkg/commands/authtoken/delete.go index 7f5d79be2..7e700f114 100644 --- a/pkg/commands/authtoken/delete.go +++ b/pkg/commands/authtoken/delete.go @@ -10,9 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -45,11 +43,6 @@ type DeleteCommand struct { // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - if !c.current && c.file == "" && c.id == "" { return fmt.Errorf("error parsing arguments: must provide either the --current, --file or --id flag") } diff --git a/pkg/commands/authtoken/describe.go b/pkg/commands/authtoken/describe.go index e4dc3c5c8..28f0789a2 100644 --- a/pkg/commands/authtoken/describe.go +++ b/pkg/commands/authtoken/describe.go @@ -5,12 +5,12 @@ import ( "io" "strings" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. @@ -37,10 +37,6 @@ type DescribeCommand struct { // Exec invokes the application logic for the command. func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/authtoken/list.go b/pkg/commands/authtoken/list.go index b9f9a014c..027a87ffb 100644 --- a/pkg/commands/authtoken/list.go +++ b/pkg/commands/authtoken/list.go @@ -10,7 +10,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -46,10 +45,6 @@ type ListCommand struct { // Exec invokes the application logic for the command. func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/backend/backend_test.go b/pkg/commands/backend/backend_test.go index dc96a93e4..fd8cea69b 100644 --- a/pkg/commands/backend/backend_test.go +++ b/pkg/commands/backend/backend_test.go @@ -477,7 +477,7 @@ SERVICE VERSION NAME ADDRESS PORT COMMENT `) + "\n" var listBackendsVerboseOutput = strings.Join([]string{ - "Fastly API token not provided", + "Fastly API token provided via config file (profile: user)", "Fastly API endpoint: https://api.fastly.com", "", "Service ID (via --service-id): 123", diff --git a/pkg/commands/compute/build_test.go b/pkg/commands/compute/build_test.go index 6143d869d..65ce4b54d 100644 --- a/pkg/commands/compute/build_test.go +++ b/pkg/commands/compute/build_test.go @@ -28,7 +28,7 @@ func TestBuildRust(t *testing.T) { scenarios := []struct { name string args []string - applicationConfig config.File + applicationConfig *config.File // a pointer so we can assert if configured fastlyManifest string cargoManifest string wantError string @@ -67,7 +67,8 @@ func TestBuildRust(t *testing.T) { { name: "build script inserted dynamically when missing", args: args("compute build --verbose"), - applicationConfig: config.File{ + applicationConfig: &config.File{ + Profiles: testutil.TokenProfile(), Language: config.Language{ Rust: config.Rust{ ToolchainConstraint: ">= 1.54.0", @@ -95,7 +96,8 @@ func TestBuildRust(t *testing.T) { { name: "build error", args: args("compute build"), - applicationConfig: config.File{ + applicationConfig: &config.File{ + Profiles: testutil.TokenProfile(), Language: config.Language{ Rust: config.Rust{ ToolchainConstraint: ">= 1.54.0", @@ -123,7 +125,8 @@ func TestBuildRust(t *testing.T) { { name: "successful build", args: args("compute build --verbose"), - applicationConfig: config.File{ + applicationConfig: &config.File{ + Profiles: testutil.TokenProfile(), Language: config.Language{ Rust: config.Rust{ ToolchainConstraint: ">= 1.54.0", @@ -214,7 +217,10 @@ func TestBuildRust(t *testing.T) { var stdout threadsafe.Buffer opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.ConfigFile = testcase.applicationConfig + + if testcase.applicationConfig != nil { + opts.ConfigFile = *testcase.applicationConfig + } opts.Versioners = app.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", @@ -254,7 +260,7 @@ func TestBuildGo(t *testing.T) { scenarios := []struct { name string args []string - applicationConfig config.File + applicationConfig *config.File fastlyManifest string wantError string wantRemediationError string @@ -291,7 +297,8 @@ func TestBuildGo(t *testing.T) { { name: "build success", args: args("compute build --verbose"), - applicationConfig: config.File{ + applicationConfig: &config.File{ + Profiles: testutil.TokenProfile(), Language: config.Language{ Go: config.Go{ TinyGoConstraint: ">= 0.26.0-0", @@ -322,7 +329,8 @@ func TestBuildGo(t *testing.T) { { name: "build error", args: args("compute build"), - applicationConfig: config.File{ + applicationConfig: &config.File{ + Profiles: testutil.TokenProfile(), Language: config.Language{ Go: config.Go{ TinyGoConstraint: ">= 0.26.0-0", @@ -401,7 +409,10 @@ func TestBuildGo(t *testing.T) { var stdout threadsafe.Buffer opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.ConfigFile = testcase.applicationConfig + + if testcase.applicationConfig != nil { + opts.ConfigFile = *testcase.applicationConfig + } opts.Versioners = app.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", diff --git a/pkg/commands/compute/deploy_test.go b/pkg/commands/compute/deploy_test.go index 0f4b492af..47b84c81a 100644 --- a/pkg/commands/compute/deploy_test.go +++ b/pkg/commands/compute/deploy_test.go @@ -108,11 +108,6 @@ func TestDeploy(t *testing.T) { wantRemediationError string wantOutput []string }{ - { - name: "no token", - args: args("compute deploy"), - wantError: "no token provided", - }, { name: "no fastly.toml manifest", args: args("compute deploy --token 123"), diff --git a/pkg/commands/compute/update.go b/pkg/commands/compute/update.go index 1a1226d27..cf274c08d 100644 --- a/pkg/commands/compute/update.go +++ b/pkg/commands/compute/update.go @@ -11,7 +11,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -63,11 +62,6 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) (err error) { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, diff --git a/pkg/commands/compute/update_test.go b/pkg/commands/compute/update_test.go index baedc3ba5..59dd71352 100644 --- a/pkg/commands/compute/update_test.go +++ b/pkg/commands/compute/update_test.go @@ -46,7 +46,7 @@ func TestUpdate(t *testing.T) { scenarios := []testutil.TestScenario{ { Name: "package API error", - Args: args("compute update -s 123 --version 1 --package pkg/package.tar.gz -t 123 --autoclone"), + Args: args("compute update -s 123 --version 1 --package pkg/package.tar.gz --autoclone"), API: mock.API{ ListVersionsFn: testutil.ListVersions, CloneVersionFn: testutil.CloneVersionResult(4), @@ -59,7 +59,7 @@ func TestUpdate(t *testing.T) { }, { Name: "success", - Args: args("compute update -s 123 --version 2 --package pkg/package.tar.gz -t 123 --autoclone"), + Args: args("compute update -s 123 --version 2 --package pkg/package.tar.gz --autoclone"), API: mock.API{ ListVersionsFn: testutil.ListVersions, CloneVersionFn: testutil.CloneVersionResult(4), diff --git a/pkg/commands/dictionary/dictionary_test.go b/pkg/commands/dictionary/dictionary_test.go index a878132bc..c1f7a8b07 100644 --- a/pkg/commands/dictionary/dictionary_test.go +++ b/pkg/commands/dictionary/dictionary_test.go @@ -466,7 +466,7 @@ var ( var updateDictionaryOutputVerbose = strings.Join( []string{ - "Fastly API token not provided", + "Fastly API token provided via config file (profile: user)", "Fastly API endpoint: https://api.fastly.com", "", "Service ID (via --service-id): 123", @@ -511,7 +511,7 @@ Deleted (UTC): 2001-02-03 04:05 `) + "\n" var describeDictionaryOutputVerbose = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/domain/domain_test.go b/pkg/commands/domain/domain_test.go index 00bfabe71..193c745ac 100644 --- a/pkg/commands/domain/domain_test.go +++ b/pkg/commands/domain/domain_test.go @@ -10,7 +10,6 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" - fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -251,14 +250,9 @@ func TestDomainValidate(t *testing.T) { Args: args("domain validate"), WantError: "error parsing arguments: required flag --version not provided", }, - { - Name: "validate missing --token flag", - Args: args("domain validate --version 3"), - WantError: fsterr.ErrNoToken.Inner.Error(), - }, { Name: "validate missing --service-id flag", - Args: args("domain validate --token 123 --version 3"), + Args: args("domain validate --version 3"), WantError: "error reading service: no service ID found", }, { @@ -266,7 +260,7 @@ func TestDomainValidate(t *testing.T) { API: mock.API{ ListVersionsFn: testutil.ListVersions, }, - Args: args("domain validate --service-id 123 --token 123 --version 3"), + Args: args("domain validate --service-id 123 --version 3"), WantError: "error parsing arguments: must provide --name flag", }, { @@ -277,7 +271,7 @@ func TestDomainValidate(t *testing.T) { return nil, testutil.Err }, }, - Args: args("domain validate --name foo.example.com --service-id 123 --token 123 --version 3"), + Args: args("domain validate --name foo.example.com --service-id 123 --version 3"), WantError: testutil.Err.Error(), }, { @@ -288,7 +282,7 @@ func TestDomainValidate(t *testing.T) { return nil, testutil.Err }, }, - Args: args("domain validate --all --service-id 123 --token 123 --version 3"), + Args: args("domain validate --all --service-id 123 --version 3"), WantError: testutil.Err.Error(), }, { @@ -297,7 +291,7 @@ func TestDomainValidate(t *testing.T) { ListVersionsFn: testutil.ListVersions, ValidateDomainFn: validateDomain, }, - Args: args("domain validate --name foo.example.com --service-id 123 --token 123 --version 3"), + Args: args("domain validate --name foo.example.com --service-id 123 --version 3"), WantOutput: validateAPISuccess(3), }, { @@ -306,7 +300,7 @@ func TestDomainValidate(t *testing.T) { ListVersionsFn: testutil.ListVersions, ValidateAllDomainsFn: validateAllDomains, }, - Args: args("domain validate --all --service-id 123 --token 123 --version 3"), + Args: args("domain validate --all --service-id 123 --version 3"), WantOutput: validateAllAPISuccess(), }, { @@ -315,7 +309,7 @@ func TestDomainValidate(t *testing.T) { ListVersionsFn: testutil.ListVersions, ValidateDomainFn: validateDomain, }, - Args: args("domain validate --name foo.example.com --service-id 123 --token 123 --version 1"), + Args: args("domain validate --name foo.example.com --service-id 123 --version 1"), WantOutput: validateAPISuccess(1), }, } @@ -375,7 +369,7 @@ SERVICE VERSION NAME COMMENT `) + "\n" var listDomainsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/domain/validate.go b/pkg/commands/domain/validate.go index 3ab7068df..25b088ab3 100644 --- a/pkg/commands/domain/validate.go +++ b/pkg/commands/domain/validate.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewValidateCommand returns a usable command registered under the parent. @@ -59,11 +59,6 @@ type ValidateCommand struct { // Exec invokes the application logic for the command. func (c *ValidateCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, diff --git a/pkg/commands/healthcheck/healthcheck_test.go b/pkg/commands/healthcheck/healthcheck_test.go index 5c38d14af..4dac9496b 100644 --- a/pkg/commands/healthcheck/healthcheck_test.go +++ b/pkg/commands/healthcheck/healthcheck_test.go @@ -317,7 +317,7 @@ SERVICE VERSION NAME METHOD HOST PATH `) + "\n" var listHealthChecksVerboseOutput = strings.Join([]string{ - "Fastly API token not provided", + "Fastly API token provided via config file (profile: user)", "Fastly API endpoint: https://api.fastly.com", "", "Service ID (via --service-id): 123", diff --git a/pkg/commands/ip/ip_test.go b/pkg/commands/ip/ip_test.go index 57e4305ec..03b1cd074 100644 --- a/pkg/commands/ip/ip_test.go +++ b/pkg/commands/ip/ip_test.go @@ -13,7 +13,7 @@ import ( func TestAllIPs(t *testing.T) { var stdout bytes.Buffer - args := testutil.Args("ip-list --token 123") + args := testutil.Args("ip-list") api := mock.API{ AllIPsFn: func() (v4, v6 fastly.IPAddrs, err error) { return []string{ diff --git a/pkg/commands/ip/root.go b/pkg/commands/ip/root.go index 979f9e377..8fb2e9200 100644 --- a/pkg/commands/ip/root.go +++ b/pkg/commands/ip/root.go @@ -5,9 +5,7 @@ import ( "io" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/text" ) @@ -27,11 +25,6 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - ipv4, ipv6, err := c.Globals.APIClient.AllIPs() if err != nil { c.Globals.ErrLog.Add(err) diff --git a/pkg/commands/logging/azureblob/azureblob_integration_test.go b/pkg/commands/logging/azureblob/azureblob_integration_test.go index 586be3912..1b81368e3 100644 --- a/pkg/commands/logging/azureblob/azureblob_integration_test.go +++ b/pkg/commands/logging/azureblob/azureblob_integration_test.go @@ -343,7 +343,7 @@ SERVICE VERSION NAME `) + "\n" var listBlobStoragesVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/bigquery/bigquery_integration_test.go b/pkg/commands/logging/bigquery/bigquery_integration_test.go index afffa14e7..3458b014b 100644 --- a/pkg/commands/logging/bigquery/bigquery_integration_test.go +++ b/pkg/commands/logging/bigquery/bigquery_integration_test.go @@ -313,7 +313,7 @@ SERVICE VERSION NAME `) + "\n" var listBigQueriesVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go index ee022b8d4..95beef2e9 100644 --- a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go +++ b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go @@ -6,10 +6,11 @@ import ( "strings" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestCloudfilesCreate(t *testing.T) { @@ -333,7 +334,7 @@ SERVICE VERSION NAME `) + "\n" var listCloudfilesVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/datadog/datadog_integration_test.go b/pkg/commands/logging/datadog/datadog_integration_test.go index 81abe9a0c..c31ca799d 100644 --- a/pkg/commands/logging/datadog/datadog_integration_test.go +++ b/pkg/commands/logging/datadog/datadog_integration_test.go @@ -310,7 +310,7 @@ SERVICE VERSION NAME `) + "\n" var listDatadogsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go index 87383fd05..e35193d7e 100644 --- a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go +++ b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go @@ -334,7 +334,7 @@ SERVICE VERSION NAME `) + "\n" var listDigitalOceansVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go index 164b2b39e..46247e280 100644 --- a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go +++ b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go @@ -338,7 +338,7 @@ SERVICE VERSION NAME `) + "\n" var listElasticsearchsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/ftp/ftp_integration_test.go b/pkg/commands/logging/ftp/ftp_integration_test.go index 97c196cca..e53ea682b 100644 --- a/pkg/commands/logging/ftp/ftp_integration_test.go +++ b/pkg/commands/logging/ftp/ftp_integration_test.go @@ -330,7 +330,7 @@ SERVICE VERSION NAME `) + "\n" var listFTPsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/gcs/gcs_integration_test.go b/pkg/commands/logging/gcs/gcs_integration_test.go index 566792684..c876a5247 100644 --- a/pkg/commands/logging/gcs/gcs_integration_test.go +++ b/pkg/commands/logging/gcs/gcs_integration_test.go @@ -329,7 +329,7 @@ SERVICE VERSION NAME `) + "\n" var listGCSsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go index 7a2d2ddfb..22be6e744 100644 --- a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go +++ b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go @@ -320,7 +320,7 @@ SERVICE VERSION NAME `) + "\n" var listGooglePubSubsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/heroku/heroku_integration_test.go b/pkg/commands/logging/heroku/heroku_integration_test.go index c11b44aa5..367771032 100644 --- a/pkg/commands/logging/heroku/heroku_integration_test.go +++ b/pkg/commands/logging/heroku/heroku_integration_test.go @@ -6,10 +6,11 @@ import ( "strings" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestHerokuCreate(t *testing.T) { @@ -309,7 +310,7 @@ SERVICE VERSION NAME `) + "\n" var listHerokusVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go index 36abb323a..20cca51fe 100644 --- a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go +++ b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go @@ -310,7 +310,7 @@ SERVICE VERSION NAME `) + "\n" var listHoneycombsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/https/https_integration_test.go b/pkg/commands/logging/https/https_integration_test.go index 4888307c7..efbaffe43 100644 --- a/pkg/commands/logging/https/https_integration_test.go +++ b/pkg/commands/logging/https/https_integration_test.go @@ -344,7 +344,7 @@ SERVICE VERSION NAME `) + "\n" var listHTTPSsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/kafka/kafka_integration_test.go b/pkg/commands/logging/kafka/kafka_integration_test.go index c45954360..60c1e833a 100644 --- a/pkg/commands/logging/kafka/kafka_integration_test.go +++ b/pkg/commands/logging/kafka/kafka_integration_test.go @@ -6,10 +6,11 @@ import ( "strings" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestKafkaCreate(t *testing.T) { @@ -355,7 +356,7 @@ SERVICE VERSION NAME `) + "\n" var listKafkasVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 @@ -404,7 +405,7 @@ Version: 1 Max batch size: 0 SASL authentication method: SASL authentication username: - SASL authentication password: + SASL authentication password: `) + " \n\n" func getKafkaOK(i *fastly.GetKafkaInput) (*fastly.Kafka, error) { diff --git a/pkg/commands/logging/kinesis/kinesis_integration_test.go b/pkg/commands/logging/kinesis/kinesis_integration_test.go index 4ad68dfb8..e0b4e4523 100644 --- a/pkg/commands/logging/kinesis/kinesis_integration_test.go +++ b/pkg/commands/logging/kinesis/kinesis_integration_test.go @@ -6,10 +6,11 @@ import ( "strings" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestKinesisCreate(t *testing.T) { @@ -350,7 +351,7 @@ SERVICE VERSION NAME `) + "\n" var listKinesesVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/loggly/loggly_integration_test.go b/pkg/commands/logging/loggly/loggly_integration_test.go index 6b12b44f8..444ebfe98 100644 --- a/pkg/commands/logging/loggly/loggly_integration_test.go +++ b/pkg/commands/logging/loggly/loggly_integration_test.go @@ -308,7 +308,7 @@ SERVICE VERSION NAME `) + "\n" var listLogglysVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go index 7dd2aa6b5..305ac3388 100644 --- a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go +++ b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go @@ -310,7 +310,7 @@ SERVICE VERSION NAME `) + "\n" var listLogshuttlesVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/newrelic/newrelic_test.go b/pkg/commands/logging/newrelic/newrelic_test.go index 3bfcff6f8..d82f3b605 100644 --- a/pkg/commands/logging/newrelic/newrelic_test.go +++ b/pkg/commands/logging/newrelic/newrelic_test.go @@ -4,10 +4,11 @@ import ( "bytes" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestNewRelicCreate(t *testing.T) { @@ -269,7 +270,7 @@ func TestNewRelicList(t *testing.T) { ListNewRelicFn: listNewRelic, }, Args: args("logging newrelic list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go index 7e3b1121a..78c220898 100644 --- a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go +++ b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go @@ -4,10 +4,11 @@ import ( "bytes" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestNewRelicOTLPCreate(t *testing.T) { @@ -269,7 +270,7 @@ func TestNewRelicList(t *testing.T) { ListNewRelicOTLPFn: listNewRelic, }, Args: args("logging newrelicotlp list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/logging/openstack/openstack_integration_test.go b/pkg/commands/logging/openstack/openstack_integration_test.go index 06889118a..cbb3078cb 100644 --- a/pkg/commands/logging/openstack/openstack_integration_test.go +++ b/pkg/commands/logging/openstack/openstack_integration_test.go @@ -336,7 +336,7 @@ SERVICE VERSION NAME `) + "\n" var listOpenstacksVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/papertrail/papertrail_integration_test.go b/pkg/commands/logging/papertrail/papertrail_integration_test.go index 8efe1529b..cf8a74239 100644 --- a/pkg/commands/logging/papertrail/papertrail_integration_test.go +++ b/pkg/commands/logging/papertrail/papertrail_integration_test.go @@ -305,7 +305,7 @@ SERVICE VERSION NAME `) + "\n" var listPapertrailsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/s3/s3_integration_test.go b/pkg/commands/logging/s3/s3_integration_test.go index 1bec4f01a..f12ad8627 100644 --- a/pkg/commands/logging/s3/s3_integration_test.go +++ b/pkg/commands/logging/s3/s3_integration_test.go @@ -397,7 +397,7 @@ SERVICE VERSION NAME `) + "\n" var listS3sVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/scalyr/scalyr_integration_test.go b/pkg/commands/logging/scalyr/scalyr_integration_test.go index 2454e83b6..4da79df7d 100644 --- a/pkg/commands/logging/scalyr/scalyr_integration_test.go +++ b/pkg/commands/logging/scalyr/scalyr_integration_test.go @@ -341,7 +341,7 @@ SERVICE VERSION NAME `) + "\n" var listScalyrsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/sftp/sftp_integration_test.go b/pkg/commands/logging/sftp/sftp_integration_test.go index d41a9fb78..f73f5253d 100644 --- a/pkg/commands/logging/sftp/sftp_integration_test.go +++ b/pkg/commands/logging/sftp/sftp_integration_test.go @@ -339,7 +339,7 @@ SERVICE VERSION NAME `) + "\n" var listSFTPsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/splunk/splunk_integration_test.go b/pkg/commands/logging/splunk/splunk_integration_test.go index 37a42f31c..a366460f3 100644 --- a/pkg/commands/logging/splunk/splunk_integration_test.go +++ b/pkg/commands/logging/splunk/splunk_integration_test.go @@ -6,10 +6,11 @@ import ( "strings" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestSplunkCreate(t *testing.T) { @@ -312,7 +313,7 @@ SERVICE VERSION NAME `) + "\n" var listSplunksVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/sumologic/sumologic_integration_test.go b/pkg/commands/logging/sumologic/sumologic_integration_test.go index 70570d88f..8da1b50b9 100644 --- a/pkg/commands/logging/sumologic/sumologic_integration_test.go +++ b/pkg/commands/logging/sumologic/sumologic_integration_test.go @@ -305,7 +305,7 @@ SERVICE VERSION NAME `) + "\n" var listSumologicsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/syslog/syslog_integration_test.go b/pkg/commands/logging/syslog/syslog_integration_test.go index 51bd334f8..4951d353b 100644 --- a/pkg/commands/logging/syslog/syslog_integration_test.go +++ b/pkg/commands/logging/syslog/syslog_integration_test.go @@ -323,7 +323,7 @@ SERVICE VERSION NAME `) + "\n" var listSyslogsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/pop/pop_test.go b/pkg/commands/pop/pop_test.go index d9598dcc0..15e4ee0b4 100644 --- a/pkg/commands/pop/pop_test.go +++ b/pkg/commands/pop/pop_test.go @@ -4,15 +4,16 @@ import ( "bytes" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestAllDatacenters(t *testing.T) { var stdout bytes.Buffer - args := testutil.Args("pops --token 123") + args := testutil.Args("pops") api := mock.API{ AllDatacentersFn: func() ([]fastly.Datacenter, error) { return []fastly.Datacenter{ diff --git a/pkg/commands/pop/root.go b/pkg/commands/pop/root.go index 9e34ccc3d..f32880ecc 100644 --- a/pkg/commands/pop/root.go +++ b/pkg/commands/pop/root.go @@ -5,9 +5,7 @@ import ( "io" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/text" ) @@ -27,11 +25,6 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - dcs, err := c.Globals.APIClient.AllDatacenters() if err != nil { c.Globals.ErrLog.Add(err) diff --git a/pkg/commands/profile/profile_test.go b/pkg/commands/profile/profile_test.go index df8eef39a..aa0d879fb 100644 --- a/pkg/commands/profile/profile_test.go +++ b/pkg/commands/profile/profile_test.go @@ -424,13 +424,25 @@ func TestList(t *testing.T) { Args: args("profile list --json"), WantOutput: `{ "bar": { + "access_token": "", + "access_token_created": 0, + "access_token_ttl": 0, "default": false, "email": "bar@example.com", + "refresh_token": "", + "refresh_token_created": 0, + "refresh_token_ttl": 0, "token": "456" }, "foo": { + "access_token": "", + "access_token_created": 0, + "access_token_ttl": 0, "default": false, "email": "foo@example.com", + "refresh_token": "", + "refresh_token_created": 0, + "refresh_token_ttl": 0, "token": "123" } }`, diff --git a/pkg/commands/purge/purge_test.go b/pkg/commands/purge/purge_test.go index d75f2af78..96e7e743b 100644 --- a/pkg/commands/purge/purge_test.go +++ b/pkg/commands/purge/purge_test.go @@ -5,28 +5,24 @@ import ( "reflect" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestPurgeAll(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing token", - Args: args("purge --all"), - WantError: "no token provided", - }, { Name: "validate missing --service-id flag", - Args: args("purge --all --token 123"), + Args: args("purge --all"), WantError: "error reading service: no service ID found", }, { Name: "validate --soft flag isn't usable", - Args: args("purge --all --service-id 123 --soft --token 456"), + Args: args("purge --all --service-id 123 --soft"), WantError: "purge-all requests cannot be done in soft mode (--soft) and will always immediately invalidate all cached content associated with the service", }, { @@ -36,7 +32,7 @@ func TestPurgeAll(t *testing.T) { return nil, testutil.Err }, }, - Args: args("purge --all --service-id 123 --token 456"), + Args: args("purge --all --service-id 123"), WantError: testutil.Err.Error(), }, { @@ -48,7 +44,7 @@ func TestPurgeAll(t *testing.T) { }, nil }, }, - Args: args("purge --all --service-id 123 --token 456"), + Args: args("purge --all --service-id 123"), WantOutput: "Purge all status: ok", }, } @@ -70,14 +66,9 @@ func TestPurgeKeys(t *testing.T) { var keys []string args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing token", - Args: args("purge --file ./testdata/keys"), - WantError: "no token provided", - }, { Name: "validate missing --service-id flag", - Args: args("purge --file ./testdata/keys --token 123"), + Args: args("purge --file ./testdata/keys"), WantError: "error reading service: no service ID found", }, { @@ -87,7 +78,7 @@ func TestPurgeKeys(t *testing.T) { return nil, testutil.Err }, }, - Args: args("purge --file ./testdata/keys --service-id 123 --token 456"), + Args: args("purge --file ./testdata/keys --service-id 123"), WantError: testutil.Err.Error(), }, { @@ -104,7 +95,7 @@ func TestPurgeKeys(t *testing.T) { }, nil }, }, - Args: args("purge --file ./testdata/keys --service-id 123 --token 456"), + Args: args("purge --file ./testdata/keys --service-id 123"), WantOutput: "KEY ID\nbar 456\nbaz 789\nfoo 123\n", }, } @@ -143,14 +134,9 @@ func assertKeys(wantError string, args []string, keys []string, t *testing.T) { func TestPurgeKey(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing token", - Args: args("purge --key foobar"), - WantError: "no token provided", - }, { Name: "validate missing --service-id flag", - Args: args("purge --key foobar --token 123"), + Args: args("purge --key foobar"), WantError: "error reading service: no service ID found", }, { @@ -160,7 +146,7 @@ func TestPurgeKey(t *testing.T) { return nil, testutil.Err }, }, - Args: args("purge --key foobar --service-id 123 --token 456"), + Args: args("purge --key foobar --service-id 123"), WantError: testutil.Err.Error(), }, { @@ -173,7 +159,7 @@ func TestPurgeKey(t *testing.T) { }, nil }, }, - Args: args("purge --key foobar --service-id 123 --token 456"), + Args: args("purge --key foobar --service-id 123"), WantOutput: "Purged key: foobar (soft: false). Status: ok, ID: 123", }, { @@ -186,7 +172,7 @@ func TestPurgeKey(t *testing.T) { }, nil }, }, - Args: args("purge --key foobar --service-id 123 --soft --token 456"), + Args: args("purge --key foobar --service-id 123 --soft"), WantOutput: "Purged key: foobar (soft: true). Status: ok, ID: 123", }, } @@ -198,6 +184,7 @@ func TestPurgeKey(t *testing.T) { opts := testutil.NewRunOpts(testcase.Args, &stdout) opts.APIClient = mock.APIClient(testcase.API) err := app.Run(opts) + t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -207,11 +194,6 @@ func TestPurgeKey(t *testing.T) { func TestPurgeURL(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing token", - Args: args("purge --url https://example.com"), - WantError: "no token provided", - }, { Name: "validate Purge API error", API: mock.API{ @@ -219,7 +201,7 @@ func TestPurgeURL(t *testing.T) { return nil, testutil.Err }, }, - Args: args("purge --service-id 123 --token 456 --url https://example.com"), + Args: args("purge --service-id 123 --url https://example.com"), WantError: testutil.Err.Error(), }, { @@ -232,7 +214,7 @@ func TestPurgeURL(t *testing.T) { }, nil }, }, - Args: args("purge --service-id 123 --token 456 --url https://example.com"), + Args: args("purge --service-id 123 --url https://example.com"), WantOutput: "Purged URL: https://example.com (soft: false). Status: ok, ID: 123", }, { @@ -245,7 +227,7 @@ func TestPurgeURL(t *testing.T) { }, nil }, }, - Args: args("purge --service-id 123 --soft --token 456 --url https://example.com"), + Args: args("purge --service-id 123 --soft --url https://example.com"), WantOutput: "Purged URL: https://example.com (soft: true). Status: ok, ID: 123", }, } diff --git a/pkg/commands/purge/root.go b/pkg/commands/purge/root.go index d6659bb71..5addaaae9 100644 --- a/pkg/commands/purge/root.go +++ b/pkg/commands/purge/root.go @@ -13,7 +13,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -63,11 +62,6 @@ type RootCommand struct { // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err diff --git a/pkg/commands/ratelimit/create.go b/pkg/commands/ratelimit/create.go index f05c0cd1c..229748385 100644 --- a/pkg/commands/ratelimit/create.go +++ b/pkg/commands/ratelimit/create.go @@ -6,13 +6,13 @@ import ( "io" "strings" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // rateLimitActionFlagOpts is a string representation of rateLimitActions @@ -124,11 +124,6 @@ type CreateCommand struct { // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } - if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/ratelimit/delete.go b/pkg/commands/ratelimit/delete.go index cd9598df9..118e5a714 100644 --- a/pkg/commands/ratelimit/delete.go +++ b/pkg/commands/ratelimit/delete.go @@ -6,9 +6,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -36,11 +34,6 @@ type DeleteCommand struct { // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - input := c.constructInput() err := c.Globals.APIClient.DeleteERL(input) diff --git a/pkg/commands/ratelimit/describe.go b/pkg/commands/ratelimit/describe.go index 708ec0f16..608d5a3a8 100644 --- a/pkg/commands/ratelimit/describe.go +++ b/pkg/commands/ratelimit/describe.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" ) @@ -40,11 +39,6 @@ type DescribeCommand struct { // Exec invokes the application logic for the command. func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } - if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/ratelimit/list.go b/pkg/commands/ratelimit/list.go index 32d4431f3..dd31824b4 100644 --- a/pkg/commands/ratelimit/list.go +++ b/pkg/commands/ratelimit/list.go @@ -4,13 +4,13 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. @@ -57,11 +57,6 @@ type ListCommand struct { // Exec invokes the application logic for the command. func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } - if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/ratelimit/ratelimit_test.go b/pkg/commands/ratelimit/ratelimit_test.go index 4b263552b..591e1b71d 100644 --- a/pkg/commands/ratelimit/ratelimit_test.go +++ b/pkg/commands/ratelimit/ratelimit_test.go @@ -22,7 +22,7 @@ func TestCreate(t *testing.T) { }, ListVersionsFn: testutil.ListVersions, }, - Args: args("rate-limit create --name example --service-id 123 --token abc --version 3"), + Args: args("rate-limit create --name example --service-id 123 --version 3"), WantError: testutil.Err.Error(), }, { @@ -36,7 +36,7 @@ func TestCreate(t *testing.T) { }, ListVersionsFn: testutil.ListVersions, }, - Args: args("rate-limit create --name example --service-id 123 --token abc --version 3"), + Args: args("rate-limit create --name example --service-id 123 --version 3"), WantOutput: "Created rate limiter 'example' (123)", }, } @@ -64,7 +64,7 @@ func TestDelete(t *testing.T) { return testutil.Err }, }, - Args: args("rate-limit delete --id 123 --token abc"), + Args: args("rate-limit delete --id 123"), WantError: testutil.Err.Error(), }, { @@ -74,7 +74,7 @@ func TestDelete(t *testing.T) { return nil }, }, - Args: args("rate-limit delete --id 123 --token abc"), + Args: args("rate-limit delete --id 123"), WantOutput: "SUCCESS: Deleted rate limiter '123'\n", }, } @@ -102,7 +102,7 @@ func TestDescribe(t *testing.T) { return nil, testutil.Err }, }, - Args: args("rate-limit describe --id 123 --token abc"), + Args: args("rate-limit describe --id 123"), WantError: testutil.Err.Error(), }, { @@ -119,7 +119,7 @@ func TestDescribe(t *testing.T) { }, nil }, }, - Args: args("rate-limit describe --id 123 --token abc"), + Args: args("rate-limit describe --id 123"), WantOutput: "\nAction: response\nClient Key: []\nFeature Revision: 0\nHTTP Methods: []\nID: 123\nLogger Type: \nName: example\nPenalty Box Duration: 20\nResponse: \nResponse Object Name: \nRPS Limit: 10\nService ID: \nURI Dictionary Name: \nVersion: 0\nWindowSize: 60\n", }, } @@ -148,7 +148,7 @@ func TestList(t *testing.T) { }, ListVersionsFn: testutil.ListVersions, }, - Args: args("rate-limit list --service-id 123 --token abc --version 3"), + Args: args("rate-limit list --service-id 123 --version 3"), WantError: testutil.Err.Error(), }, { @@ -168,7 +168,7 @@ func TestList(t *testing.T) { }, ListVersionsFn: testutil.ListVersions, }, - Args: args("rate-limit list --service-id 123 --token abc --version 3"), + Args: args("rate-limit list --service-id 123 --version 3"), WantOutput: "ID NAME ACTION RPS LIMIT WINDOW SIZE PENALTY BOX DURATION\n123 example response 10 60 20\n", }, } @@ -196,7 +196,7 @@ func TestUpdate(t *testing.T) { return nil, testutil.Err }, }, - Args: args("rate-limit update --id 123 --name example --token abc"), + Args: args("rate-limit update --id 123 --name example"), WantError: testutil.Err.Error(), }, { @@ -209,7 +209,7 @@ func TestUpdate(t *testing.T) { }, nil }, }, - Args: args("rate-limit update --id 123 --name example --token abc"), + Args: args("rate-limit update --id 123 --name example"), WantOutput: "Updated rate limiter 'example' (123)", }, } diff --git a/pkg/commands/ratelimit/update.go b/pkg/commands/ratelimit/update.go index 622939393..74421ae4f 100644 --- a/pkg/commands/ratelimit/update.go +++ b/pkg/commands/ratelimit/update.go @@ -11,7 +11,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -75,10 +74,6 @@ type UpdateCommand struct { // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/service/service_test.go b/pkg/commands/service/service_test.go index d7f8942ea..dd27519db 100644 --- a/pkg/commands/service/service_test.go +++ b/pkg/commands/service/service_test.go @@ -445,7 +445,7 @@ Bar 456 wasm 1 2015-03-14 12:59 `) + "\n" var listServicesVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service 1/3 @@ -592,7 +592,7 @@ Versions: 2 `) + "\n" var describeServiceVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 @@ -702,7 +702,7 @@ Versions: 2 `) + "\n" var searchServiceVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com ID: 123 diff --git a/pkg/commands/serviceauth/service_test.go b/pkg/commands/serviceauth/service_test.go index bd2187463..ede9c7099 100644 --- a/pkg/commands/serviceauth/service_test.go +++ b/pkg/commands/serviceauth/service_test.go @@ -99,7 +99,7 @@ func TestServiceAuthList(t *testing.T) { { args: args("service-auth list --verbose"), api: mock.API{ListServiceAuthorizationsFn: listServiceAuthOK}, - wantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nAuth ID: 123\nUser ID: 456\nService ID: 789\nPermission: read_only\n", + wantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nAuth ID: 123\nUser ID: 456\nService ID: 789\nPermission: read_only\n", }, } for testcaseIdx := range scenarios { diff --git a/pkg/commands/serviceversion/serviceversion_test.go b/pkg/commands/serviceversion/serviceversion_test.go index fc5413d82..74e6c7e3d 100644 --- a/pkg/commands/serviceversion/serviceversion_test.go +++ b/pkg/commands/serviceversion/serviceversion_test.go @@ -310,7 +310,7 @@ NUMBER ACTIVE LAST EDITED (UTC) `) + "\n" var listVersionsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Service ID (via --service-id): 123 diff --git a/pkg/commands/tls/custom/certificate/certificate_test.go b/pkg/commands/tls/custom/certificate/certificate_test.go index 0b72ec66b..35ae0be11 100644 --- a/pkg/commands/tls/custom/certificate/certificate_test.go +++ b/pkg/commands/tls/custom/certificate/certificate_test.go @@ -5,10 +5,11 @@ import ( "fmt" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -195,7 +196,7 @@ func TestList(t *testing.T) { }, }, Args: args("tls-custom certificate list --verbose"), - WantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nID: " + mockResponseID + "\nIssued to: " + mockFieldValue + "\nIssuer: " + mockFieldValue + "\nName: " + mockFieldValue + "\nReplace: true\nSerial number: " + mockFieldValue + "\nSignature algorithm: " + mockFieldValue + "\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nID: " + mockResponseID + "\nIssued to: " + mockFieldValue + "\nIssuer: " + mockFieldValue + "\nName: " + mockFieldValue + "\nReplace: true\nSerial number: " + mockFieldValue + "\nSignature algorithm: " + mockFieldValue + "\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/user/create.go b/pkg/commands/user/create.go index 55c6826ee..d106438f7 100644 --- a/pkg/commands/user/create.go +++ b/pkg/commands/user/create.go @@ -3,13 +3,12 @@ package user import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. @@ -41,11 +40,6 @@ type CreateCommand struct { // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - input := c.constructInput() r, err := c.Globals.APIClient.CreateUser(input) diff --git a/pkg/commands/user/delete.go b/pkg/commands/user/delete.go index 67892e3e8..d59e988c2 100644 --- a/pkg/commands/user/delete.go +++ b/pkg/commands/user/delete.go @@ -3,13 +3,12 @@ package user import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. @@ -32,11 +31,6 @@ type DeleteCommand struct { // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - input := c.constructInput() err := c.Globals.APIClient.DeleteUser(input) diff --git a/pkg/commands/user/describe.go b/pkg/commands/user/describe.go index f5d61313d..6ba6085f7 100644 --- a/pkg/commands/user/describe.go +++ b/pkg/commands/user/describe.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. @@ -36,11 +36,6 @@ type DescribeCommand struct { // Exec invokes the application logic for the command. func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } - if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/user/list.go b/pkg/commands/user/list.go index d2a28cabf..6336517b9 100644 --- a/pkg/commands/user/list.go +++ b/pkg/commands/user/list.go @@ -4,13 +4,13 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. @@ -40,10 +40,6 @@ type ListCommand struct { // Exec invokes the application logic for the command. func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return fsterr.ErrNoToken - } if c.Globals.Verbose() && c.JSONOutput.Enabled { return fsterr.ErrInvalidVerboseJSONCombo } diff --git a/pkg/commands/user/update.go b/pkg/commands/user/update.go index 75daf2a3f..f9697be94 100644 --- a/pkg/commands/user/update.go +++ b/pkg/commands/user/update.go @@ -4,13 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. @@ -42,11 +41,6 @@ type UpdateCommand struct { // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { - _, s := c.Globals.Token() - if s == lookup.SourceUndefined { - return errors.ErrNoToken - } - if c.reset { input, err := c.constructInputReset() if err != nil { diff --git a/pkg/commands/user/user_test.go b/pkg/commands/user/user_test.go index 479d92840..e0a246bb4 100644 --- a/pkg/commands/user/user_test.go +++ b/pkg/commands/user/user_test.go @@ -8,7 +8,6 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -23,7 +22,7 @@ func TestCreate(t *testing.T) { return nil, testutil.Err }, }, - Args: args("user create --login foo@example.com --name foobar --token 123"), + Args: args("user create --login foo@example.com --name foobar"), WantError: testutil.Err.Error(), }, { @@ -36,7 +35,7 @@ func TestCreate(t *testing.T) { }, nil }, }, - Args: args("user create --login foo@example.com --name foobar --token 123"), + Args: args("user create --login foo@example.com --name foobar"), WantOutput: "Created user 'foobar' (role: user)", }, } @@ -62,11 +61,6 @@ func TestDelete(t *testing.T) { Args: args("user delete"), WantError: "error parsing arguments: required flag --id not provided", }, - { - Name: "validate missing --token flag", - Args: args("user delete --id foo123"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate DeleteUser API error", API: mock.API{ @@ -74,7 +68,7 @@ func TestDelete(t *testing.T) { return testutil.Err }, }, - Args: args("user delete --id foo123 --token 123"), + Args: args("user delete --id foo123"), WantError: testutil.Err.Error(), }, { @@ -84,7 +78,7 @@ func TestDelete(t *testing.T) { return nil }, }, - Args: args("user delete --id foo123 --token 123"), + Args: args("user delete --id foo123"), WantOutput: "Deleted user (id: foo123)", }, } @@ -105,14 +99,9 @@ func TestDelete(t *testing.T) { func TestDescribe(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing --token flag", - Args: args("user describe"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate missing --id flag", - Args: args("user describe --token 123"), + Args: args("user describe"), WantError: "error parsing arguments: must provide --id flag", }, { @@ -122,7 +111,7 @@ func TestDescribe(t *testing.T) { return nil, testutil.Err }, }, - Args: args("user describe --id 123 --token 123"), + Args: args("user describe --id 123"), WantError: testutil.Err.Error(), }, { @@ -132,7 +121,7 @@ func TestDescribe(t *testing.T) { return nil, testutil.Err }, }, - Args: args("user describe --current --token 123"), + Args: args("user describe --current"), WantError: testutil.Err.Error(), }, { @@ -140,7 +129,7 @@ func TestDescribe(t *testing.T) { API: mock.API{ GetUserFn: getUser, }, - Args: args("user describe --id 123 --token 123"), + Args: args("user describe --id 123"), WantOutput: describeUserOutput(), }, { @@ -148,7 +137,7 @@ func TestDescribe(t *testing.T) { API: mock.API{ GetCurrentUserFn: getCurrentUser, }, - Args: args("user describe --current --token 123"), + Args: args("user describe --current"), WantOutput: describeCurrentUserOutput(), }, } @@ -169,14 +158,9 @@ func TestDescribe(t *testing.T) { func TestList(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing --token flag", - Args: args("user list --customer-id abc"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate missing --customer-id flag", - Args: args("user list --token 123"), + Args: args("user list"), WantError: "error reading customer ID: no customer ID found", }, { @@ -186,7 +170,7 @@ func TestList(t *testing.T) { return nil, testutil.Err }, }, - Args: args("user list --customer-id abc --token 123"), + Args: args("user list --customer-id abc"), WantError: testutil.Err.Error(), }, { @@ -194,7 +178,7 @@ func TestList(t *testing.T) { API: mock.API{ ListCustomerUsersFn: listUsers, }, - Args: args("user list --customer-id abc --token 123"), + Args: args("user list --customer-id abc"), WantOutput: listOutput(), }, { @@ -202,7 +186,7 @@ func TestList(t *testing.T) { API: mock.API{ ListCustomerUsersFn: listUsers, }, - Args: args("user list --customer-id abc --token 123 --verbose"), + Args: args("user list --customer-id abc --verbose"), WantOutput: listVerboseOutput(), }, } @@ -223,29 +207,24 @@ func TestList(t *testing.T) { func TestUpdate(t *testing.T) { args := testutil.Args scenarios := []testutil.TestScenario{ - { - Name: "validate missing --token flag", - Args: args("user update --id 123"), - WantError: errors.ErrNoToken.Inner.Error(), - }, { Name: "validate missing --id flag", - Args: args("user update --token 123"), + Args: args("user update"), WantError: "error parsing arguments: must provide --id flag", }, { Name: "validate missing --name and --role flags", - Args: args("user update --id 123 --token 123"), + Args: args("user update --id 123"), WantError: "error parsing arguments: must provide either the --name or --role with the --id flag", }, { Name: "validate missing --login flag with --password-reset", - Args: args("user update --password-reset --token 123"), + Args: args("user update --password-reset"), WantError: "error parsing arguments: must provide --login when requesting a password reset", }, { Name: "validate invalid --role value", - Args: args("user update --id 123 --role foobar --token 123"), + Args: args("user update --id 123 --role foobar"), WantError: "error parsing arguments: enum value must be one of user,billing,engineer,superuser, got 'foobar'", }, { @@ -255,7 +234,7 @@ func TestUpdate(t *testing.T) { return nil, testutil.Err }, }, - Args: args("user update --id 123 --name foo --token 123"), + Args: args("user update --id 123 --name foo"), WantError: testutil.Err.Error(), }, { @@ -265,7 +244,7 @@ func TestUpdate(t *testing.T) { return testutil.Err }, }, - Args: args("user update --id 123 --login foo@example.com --password-reset --token 123"), + Args: args("user update --id 123 --login foo@example.com --password-reset"), WantError: testutil.Err.Error(), }, { @@ -279,7 +258,7 @@ func TestUpdate(t *testing.T) { }, nil }, }, - Args: args("user update --id 123 --name foo --role engineer --token 123"), + Args: args("user update --id 123 --name foo --role engineer"), WantOutput: "Updated user 'foo' (role: engineer)", }, { @@ -289,7 +268,7 @@ func TestUpdate(t *testing.T) { return nil }, }, - Args: args("user update --id 123 --login foo@example.com --password-reset --token 123"), + Args: args("user update --id 123 --login foo@example.com --password-reset"), WantOutput: "Reset user password (login: foo@example.com)", }, } @@ -407,7 +386,7 @@ bar@example.com bar superuser false current123 } func listVerboseOutput() string { - return fmt.Sprintf(`Fastly API token provided via --token + return fmt.Sprintf(`Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com %s%s`, describeUserOutput(), describeCurrentUserOutput()) diff --git a/pkg/commands/vcl/custom/custom_test.go b/pkg/commands/vcl/custom/custom_test.go index 2bb54deed..f44aecffb 100644 --- a/pkg/commands/vcl/custom/custom_test.go +++ b/pkg/commands/vcl/custom/custom_test.go @@ -4,10 +4,11 @@ import ( "bytes" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestVCLCustomCreate(t *testing.T) { @@ -353,7 +354,7 @@ func TestVCLCustomList(t *testing.T) { ListVCLsFn: listVCLs, }, Args: args("vcl custom list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nMain: false\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nMain: false\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/vcl/snippet/snippet_test.go b/pkg/commands/vcl/snippet/snippet_test.go index f42ad410f..3c29d8d1e 100644 --- a/pkg/commands/vcl/snippet/snippet_test.go +++ b/pkg/commands/vcl/snippet/snippet_test.go @@ -409,7 +409,7 @@ func TestVCLSnippetList(t *testing.T) { ListSnippetsFn: listSnippets, }, Args: args("vcl snippet list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token not provided\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: abc\nPriority: 0\nDynamic: true\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: abc\nPriority: 0\nDynamic: false\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: abc\nPriority: 0\nDynamic: true\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: abc\nPriority: 0\nDynamic: false\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/whoami/root.go b/pkg/commands/whoami/root.go index 152841343..c29b7e876 100644 --- a/pkg/commands/whoami/root.go +++ b/pkg/commands/whoami/root.go @@ -9,9 +9,7 @@ import ( "strings" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/useragent" ) @@ -38,11 +36,9 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { return fmt.Errorf("error constructing API request: %w", err) } - token, source := c.Globals.Token() - if source == lookup.SourceUndefined { - return errors.ErrNoToken - } + token, _ := c.Globals.Token() + // FIXME: Should we set `Authorization: Bearer `? req.Header.Set("Fastly-Key", token) req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", useragent.Name) diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index 7917fa72a..658855c7a 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -28,39 +28,33 @@ func TestWhoami(t *testing.T) { wantError string wantOutput string }{ - { - name: "no token", - args: args("whoami"), - client: verifyClient(basicResponse), - wantError: "no token provided", - }, { name: "basic response", - args: args("--token=x whoami"), + args: args("whoami"), client: verifyClient(basicResponse), wantOutput: basicOutput, }, { name: "basic response verbose", - args: args("--token=x whoami -v"), + args: args("whoami -v"), client: verifyClient(basicResponse), wantOutput: basicOutputVerbose, }, { name: "500 from API", - args: args("--token=x whoami"), + args: args("whoami"), client: codeClient{code: http.StatusInternalServerError}, wantError: "error from API: 500 Internal Server Error", }, { name: "local error", - args: args("--token=x whoami"), + args: args("whoami"), client: errorClient{err: errors.New("some network failure")}, wantError: "error executing API request: some network failure", }, { name: "alternative endpoint from flag", - args: args("--token=x whoami --endpoint=https://staging.fastly.com -v"), + args: args("whoami --endpoint=https://staging.fastly.com -v"), client: verifyClient(basicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, "Fastly API endpoint: https://api.fastly.com", @@ -69,7 +63,7 @@ func TestWhoami(t *testing.T) { }, { name: "alternative endpoint from environment", - args: args("--token=x whoami -v"), + args: args("whoami -v"), env: config.Environment{Endpoint: "https://alternative.example.com"}, client: verifyClient(basicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, @@ -84,6 +78,8 @@ func TestWhoami(t *testing.T) { opts.Env = testcase.env opts.HTTPClient = testcase.client err := app.Run(opts) + opts.ConfigFile = config.File{} + t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -145,7 +141,7 @@ var basicResponse = whoami.VerifyResponse{ var basicOutput = "Alice Programmer \n" var basicOutputVerbose = strings.TrimSpace(` -Fastly API token provided via --token +Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com Customer ID: abc diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 0ff1f2cfe..20ab22858 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -229,8 +229,17 @@ func TestUseStatic(t *testing.T) { if strings.Contains(string(data), "[user]") { t.Error("expected legacy [user] section to be removed") } - if !strings.Contains(string(data), "[profile.user]\ndefault = true\nemail = \"testing@fastly.com\"\ntoken = \"foobar\"") { - t.Error("expected legacy [user] section to be migrated to [profile.user]") + if !strings.Contains(string(data), `[profile.user] +access_token = "" +access_token_created = 0 +access_token_ttl = 0 +default = true +email = "testing@fastly.com" +refresh_token = "" +refresh_token_created = 0 +refresh_token_ttl = 0 +token = "foobar"`) { + t.Errorf("expected legacy [user] section to be migrated to [profile.user]: %s", string(data)) } // Validate that invalid static configuration returns a specific error. diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index 15e4af1bc..a5075e53a 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -69,9 +69,11 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { } return app.RunOpts{ - APIClient: mock.APIClient(mock.API{}), - Args: args, - ConfigFile: config.File{}, + APIClient: mock.APIClient(mock.API{}), + Args: args, + ConfigFile: config.File{ + Profiles: TokenProfile(), + }, ConfigPath: configPath, Env: config.Environment{}, ErrLog: errors.Log, @@ -83,3 +85,21 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { Stdout: stdout, } } + +func TokenProfile() config.Profiles { + return config.Profiles{ + // IMPORTANT: Tests mock the token to prevent runtime panics. + // + // Tokens are now interactively handled unless a token is provided + // directly via the --token flag or the FASTLY_API_TOKEN env variable. + // + // We force the CLI to skip the interactive prompts by setting a default + // user profile and making sure the timestamp is not expired. + "user": &config.Profile{ + AccessTokenCreated: 9999999999, // Year: 2286 + Default: true, + Email: "test@example.com", + Token: "mock-token", + }, + } +} From b1658eba651659dc5fd62f8b10565e64e90e3859 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 15:03:27 +0100 Subject: [PATCH 014/115] refactor(app): move kingpin config --- pkg/app/run.go | 78 +++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 8a1f56013..74838ca0d 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -56,42 +56,7 @@ func Run(opts RunOpts) error { Output: opts.Stdout, } - // Set up the main application root, including global flags, and then each - // of the subcommands. Note that we deliberately don't use some of the more - // advanced features of the kingpin.Application flags, like env var - // bindings, because we need to do things like track where a config - // parameter came from. - app := kingpin.New("fastly", "A tool to interact with the Fastly API") - app.Writers(opts.Stdout, io.Discard) // don't let kingpin write error output - app.UsageContext(&kingpin.UsageContext{ - Template: VerboseUsageTemplate, - Funcs: UsageTemplateFuncs, - }) - - // Prevent kingpin from calling os.Exit, this gives us greater control over - // error states and output control flow. - app.Terminate(nil) - - // WARNING: kingpin has no way of decorating flags as being "global" - // therefore if you add/remove a global flag you will also need to update - // the globalFlags map in pkg/app/usage.go which is used for usage rendering. - // You should also update `IsGlobalFlagsOnly` in ../cmd/cmd.go - // - // NOTE: Global flags (long and short) MUST be unique. - // A subcommand can't define a flag that is already global. - // Kingpin will otherwise trigger a runtime panic 🎉 - // Interestingly, short flags can be reused but only across subcommands. - tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.Token) - app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&g.Flags.AcceptDefaults) - app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&g.Flags.AutoYes) - // IMPORTANT: `--debug` is a built-in Kingpin flag so we can't use that. - app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&g.Flags.Debug) - app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&g.Flags.Endpoint) - app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&g.Flags.NonInteractive) - app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&g.Flags.Profile) - app.Flag("quiet", "Silence all output except direct command output. This won't prevent interactive prompts (see: --accept-defaults, --auto-yes, --non-interactive)").Short('q').BoolVar(&g.Flags.Quiet) - app.Flag("token", tokenHelp).HintAction(env.Vars).Short('t').StringVar(&g.Flags.Token) - app.Flag("verbose", "Verbose logging").Short('v').BoolVar(&g.Flags.Verbose) + app := configureKingpin(opts.Stdout, &g) commands := defineCommands(app, &g, *opts.Manifest, opts) command, commandName, err := processCommandInput(opts, app, &g, commands) @@ -398,6 +363,47 @@ type RunOpts struct { Versioners Versioners } +func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { + // Set up the main application root, including global flags, and then each + // of the subcommands. Note that we deliberately don't use some of the more + // advanced features of the kingpin.Application flags, like env var + // bindings, because we need to do things like track where a config + // parameter came from. + app := kingpin.New("fastly", "A tool to interact with the Fastly API") + app.Writers(out, io.Discard) // don't let kingpin write error output + app.UsageContext(&kingpin.UsageContext{ + Template: VerboseUsageTemplate, + Funcs: UsageTemplateFuncs, + }) + + // Prevent kingpin from calling os.Exit, this gives us greater control over + // error states and output control flow. + app.Terminate(nil) + + // WARNING: kingpin has no way of decorating flags as being "global" + // therefore if you add/remove a global flag you will also need to update + // the globalFlags map in pkg/app/usage.go which is used for usage rendering. + // You should also update `IsGlobalFlagsOnly` in ../cmd/cmd.go + // + // NOTE: Global flags (long and short) MUST be unique. + // A subcommand can't define a flag that is already global. + // Kingpin will otherwise trigger a runtime panic 🎉 + // Interestingly, short flags can be reused but only across subcommands. + tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.Token) + app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&g.Flags.AcceptDefaults) + app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&g.Flags.AutoYes) + // IMPORTANT: `--debug` is a built-in Kingpin flag so we can't use that. + app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&g.Flags.Debug) + app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&g.Flags.Endpoint) + app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&g.Flags.NonInteractive) + app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&g.Flags.Profile) + app.Flag("quiet", "Silence all output except direct command output. This won't prevent interactive prompts (see: --accept-defaults, --auto-yes, --non-interactive)").Short('q').BoolVar(&g.Flags.Quiet) + app.Flag("token", tokenHelp).HintAction(env.Vars).Short('t').StringVar(&g.Flags.Token) + app.Flag("verbose", "Verbose logging").Short('v').BoolVar(&g.Flags.Verbose) + + return app +} + // APIClientFactory creates a Fastly API client (modeled as an api.Interface) // from a user-provided API token. It exists as a type in order to parameterize // the Run helper with it: in the real CLI, we can use NewClient from the Fastly From 7912ed7d7d8c33cbd9ab75bc3f741d1a75e2a948 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 15:18:36 +0100 Subject: [PATCH 015/115] refactor(app): small doc clean-ups --- pkg/app/run.go | 4 ++-- pkg/profile/profile.go | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 74838ca0d..5f3e16545 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -57,12 +57,12 @@ func Run(opts RunOpts) error { } app := configureKingpin(opts.Stdout, &g) - commands := defineCommands(app, &g, *opts.Manifest, opts) command, commandName, err := processCommandInput(opts, app, &g, commands) if err != nil { return err } + // We short-circuit the execution for specific cases: // // - cmd.ArgsIsHelpJSON() == true @@ -95,7 +95,6 @@ func Run(opts RunOpts) error { } token, tokenSource := g.Token() - authWarningMsg := "No API token could be found" // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. @@ -282,6 +281,7 @@ func Run(opts RunOpts) error { ) } + // Handle any profile override. token, err = profile.Init(token, opts.Manifest, &g, opts.Stdin, opts.Stdout) if err != nil { return err diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index e9d185f63..0350d7d17 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -103,7 +103,8 @@ func Edit(name string, p config.Profiles, opts ...EditOption) (config.Profiles, return p, ok } -// Init checks if a profile flag is provided and potentially mutates token. +// Init checks if a profile flag is provided and potentially mutates the +// provided token. // // NOTE: If the specified profile doesn't exist, then we'll let the user decide // if the default profile (if available) is acceptable to use instead. @@ -116,9 +117,12 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W profile = g.Flags.Profile } - // If the user has specified no profile override, via flag nor manifest, then - // we'll just return the token that has potentially been found within the - // CLI's application configuration file. + // If the user has not specified a profile override, either via the --profile + // flag or the manifest `profile` field, then we'll return back the token that + // was passed into this function. The token passed in was either provided by + // the --token flag or the FASTLY_API_TOKEN env var or potentially was found + // within the CLI's application configuration file (and if none of those, then + // the user would have authenticated via the interactive OAuth flow). if profile == "" { return token, nil } @@ -129,7 +133,6 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W } msg := fmt.Sprintf(DoesNotExist, profile) - name, p = Default(g.Config.Profiles) if name == "" { msg = fmt.Sprintf("%s (no account profiles configured)", msg) @@ -144,12 +147,10 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W // first letter so the warning reads like a proper sentence (where as golang // errors should always be lowercase). msg = fmt.Sprintf("%s%s. ", bytes.ToUpper([]byte(msg[:1])), msg[1:]) - msg = fmt.Sprintf("%sThe default profile '%s' (%s) will be used.", msg, name, p.Email) if !g.Flags.AutoYes { text.Warning(out, msg) - label := "\nWould you like to continue? [y/N] " cont, err := text.AskYesNo(out, label, in) if err != nil { @@ -159,7 +160,6 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W return token, errors.New("command execution cancelled") } } - text.Break(out) return p.Token, nil } From a8d68be2f656d52a5bd0e8373d01df0b80a2845d Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 15:51:00 +0100 Subject: [PATCH 016/115] refactor: support flag/env-var override for accounts endpoint --- pkg/app/run.go | 6 +++-- pkg/auth/auth.go | 39 ++++++++++++------------------- pkg/commands/authenticate/root.go | 8 ++++--- pkg/config/config.go | 8 +++++-- pkg/env/env.go | 3 +++ pkg/global/global.go | 21 +++++++++++++++++ 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 5f3e16545..16084ae5e 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -146,12 +146,13 @@ func Run(opts RunOpts) error { text.Info(opts.Stdout, "Your access token has now expired. We will attempt to refresh it") } text.Break(opts.Stdout) - updated, err := auth.RefreshAccessToken(profileData.RefreshToken) + account, _ := g.Account() + updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) if err != nil { return fmt.Errorf("failed to refresh access token: %w", err) } - claims, err := auth.VerifyJWTSignature(updated.AccessToken) + claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) if err != nil { return fmt.Errorf("failed to verify refreshed JWT: %w", err) } @@ -391,6 +392,7 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // Interestingly, short flags can be reused but only across subcommands. tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.Token) app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&g.Flags.AcceptDefaults) + app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&g.Flags.Account) app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&g.Flags.AutoYes) // IMPORTANT: `--debug` is a built-in Kingpin flag so we can't use that. app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&g.Flags.Debug) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 275d4cd4a..947fce83a 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -18,28 +18,19 @@ import ( fsterr "github.com/fastly/cli/pkg/errors" ) -// FIXME: UPDATE WITH PUBLIC ENDPOINT👇 -// NOTE: https://keycloak.ext.awsuse2.dev.k8s.secretcdn.net/realms/fastly/.well-known/openid-configuration - // Remediation is a generic remediation message for an error authorizing. const Remediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" -// ServerURL is the auth provider's device code URL. -// FIXME: Add --accounts override (accounts.fastly.com) -const ServerURL = "https://accounts.secretcdn-stg.net" - // ClientID is the auth provider's Client ID. const ClientID = "fastly-cli" -// Audience is the unique identifier of the API your app wants to access. -// FIXME: Use --endpoint override (api.fastly.com) + allow env var override -const Audience = "https://api.secretcdn-stg.net/" - // RedirectURL is the endpoint the auth provider will pass an authorization code to. const RedirectURL = "http://localhost:8080/callback" // Server is a local server responsible for authentication processing. type Server struct { + // AccountEndpoint is the accounts endpoint. + AccountEndpoint string // HTTPClient is a HTTP client used to call the API to exchange the access // token for a session token. HTTPClient api.HTTPClient @@ -49,8 +40,8 @@ type Server struct { Router *http.ServeMux // Verifier represents an OAuth PKCE code verifier that uses the S256 challenge method Verifier *oidc.S256Verifier - // SessionEndpoint is the API endpoint host. - SessionEndpoint string + // APIEndpoint is the API endpoint. + APIEndpoint string } // Start starts a local server for handling authentication processing. @@ -91,7 +82,7 @@ func (s *Server) handleCallback() http.HandlerFunc { // Exchange the authorization code and the code verifier for a JWT. // NOTE: I use the identifier `j` to avoid overlap with the `jwt` package. codeVerifier := s.Verifier.Verifier() - j, err := GetJWT(codeVerifier, authorizationCode) + j, err := GetJWT(s.AccountEndpoint, codeVerifier, authorizationCode) if err != nil || j.AccessToken == "" || j.IDToken == "" { fmt.Fprint(w, "ERROR: failed to exchange code for JWT\n") s.Result <- AuthorizationResult{ @@ -100,7 +91,7 @@ func (s *Server) handleCallback() http.HandlerFunc { return } - claims, err := VerifyJWTSignature(j.AccessToken) + claims, err := VerifyJWTSignature(s.AccountEndpoint, j.AccessToken) if err != nil { s.Result <- AuthorizationResult{ Err: err, @@ -118,7 +109,7 @@ func (s *Server) handleCallback() http.HandlerFunc { // Exchange the access token for an API token resp, err := undocumented.Call(undocumented.CallOptions{ - APIEndpoint: s.SessionEndpoint, + APIEndpoint: s.APIEndpoint, HTTPClient: s.HTTPClient, HTTPHeaders: []undocumented.HTTPHeader{ { @@ -189,7 +180,7 @@ type AuthorizationResult struct { } // GenURL constructs the required authorization_endpoint path. -func GenURL(verifier *oidc.S256Verifier) (string, error) { +func GenURL(accountEndpoint, apiEndpoint string, verifier *oidc.S256Verifier) (string, error) { challenge, err := oidc.CreateCodeChallenge(verifier) if err != nil { return "", err @@ -201,14 +192,14 @@ func GenURL(verifier *oidc.S256Verifier) (string, error) { "&response_type=code&client_id=%s"+ "&code_challenge=%s"+ "&code_challenge_method=S256&redirect_uri=%s", - ServerURL, Audience, ClientID, challenge, RedirectURL) + accountEndpoint, apiEndpoint, ClientID, challenge, RedirectURL) return authorizationURL, nil } // GenJWT constructs and calls the token_endpoint path, returning a JWT // containing the access and refresh tokens and associated TTLs. -func GetJWT(codeVerifier, authorizationCode string) (JWT, error) { +func GetJWT(accountEndpoint, codeVerifier, authorizationCode string) (JWT, error) { path := "/realms/fastly/protocol/openid-connect/token" payload := fmt.Sprintf( @@ -219,7 +210,7 @@ func GetJWT(codeVerifier, authorizationCode string) (JWT, error) { "http://localhost:8080/callback", // NOTE: not redirected to, just a security check. ) - req, err := http.NewRequest("POST", ServerURL+path, strings.NewReader(payload)) + req, err := http.NewRequest("POST", accountEndpoint+path, strings.NewReader(payload)) if err != nil { return JWT{}, err } @@ -270,13 +261,13 @@ type JWT struct { } // VerifyJWTSignature calls the jwks_uri endpoint and extracts its claims. -func VerifyJWTSignature(token string) (claims map[string]any, err error) { +func VerifyJWTSignature(accountEndpoint, token string) (claims map[string]any, err error) { ctx := context.Background() path := "/realms/fastly/protocol/openid-connect/certs" // NOTE: The last argument is optional and is for validating the JWKs endpoint // (which we don't need to do, so we pass an empty string) - keySet, err := jwt.NewJSONWebKeySet(ctx, ServerURL+path, "") + keySet, err := jwt.NewJSONWebKeySet(ctx, accountEndpoint+path, "") if err != nil { return claims, fmt.Errorf("failed to verify signature of access token: %w", err) } @@ -291,7 +282,7 @@ func VerifyJWTSignature(token string) (claims map[string]any, err error) { // RefreshAccessToken constructs and calls the token_endpoint with the // refresh token so we can refresh and return the access token. -func RefreshAccessToken(refreshToken string) (JWT, error) { +func RefreshAccessToken(accountEndpoint, refreshToken string) (JWT, error) { path := "/realms/fastly/protocol/openid-connect/token" payload := fmt.Sprintf( @@ -300,7 +291,7 @@ func RefreshAccessToken(refreshToken string) (JWT, error) { refreshToken, ) - req, err := http.NewRequest("POST", ServerURL+path, strings.NewReader(payload)) + req, err := http.NewRequest("POST", accountEndpoint+path, strings.NewReader(payload)) if err != nil { return JWT{}, err } diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 94d6e7a37..a455bc3af 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -43,13 +43,15 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { } result := make(chan auth.AuthorizationResult) - endpoint, _ := c.Globals.Endpoint() + apiEndpoint, _ := c.Globals.Endpoint() + accountEndpoint, _ := c.Globals.Account() s := auth.Server{ + APIEndpoint: apiEndpoint, + AccountEndpoint: accountEndpoint, HTTPClient: c.Globals.HTTPClient, Result: result, Router: http.NewServeMux(), - SessionEndpoint: endpoint, Verifier: verifier, } s.Routes() @@ -69,7 +71,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { text.Info(out, "Starting a local server to handle the authentication flow.") - authorizationURL, err := auth.GenURL(verifier) + authorizationURL, err := auth.GenURL(accountEndpoint, apiEndpoint, verifier) if err != nil { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to generate an authorization URL: %w", err), diff --git a/pkg/config/config.go b/pkg/config/config.go index bf3d2c051..953fcf0e7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -59,7 +59,8 @@ type LegacyUser struct { // Fastly represents fastly specific configuration. type Fastly struct { - APIEndpoint string `toml:"api_endpoint"` + APIEndpoint string `toml:"api_endpoint"` + AccountEndpoint string `toml:"account_endpoint"` } // WasmMetadata represents what metadata will be collected. @@ -439,6 +440,8 @@ func (f *File) Write(path string) error { // Environment represents all of the configuration parameters that can come // from environment variables. type Environment struct { + // Account is the env var we look in for the Accounts endpoint. + Account string // Token is the env var we look in for the Fastly API token. Token string // Endpoint is the env var we look in for the API endpoint. @@ -451,8 +454,9 @@ type Environment struct { // Read populates the fields from the provided environment. func (e *Environment) Read(state map[string]string) { - e.Token = state[env.Token] + e.Account = state[env.Account] e.Endpoint = state[env.Endpoint] + e.Token = state[env.Token] e.WasmMetadataDisable = state[env.WasmMetadataDisable] } diff --git a/pkg/env/env.go b/pkg/env/env.go index 86b150a66..cdc86498c 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -19,6 +19,9 @@ const ( // Endpoint is the env var we look in for the API endpoint. Endpoint = "FASTLY_API_ENDPOINT" + // Account is the env var we look in for the Accounts endpoint. + Account = "FASTLY_ACCOUNT_ENDPOINT" + // ServiceID is the env var we look in for the required Service ID. ServiceID = "FASTLY_SERVICE_ID" diff --git a/pkg/global/global.go b/pkg/global/global.go index 35afd303b..419251b83 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -13,6 +13,9 @@ import ( // DefaultEndpoint is the default Fastly API endpoint. const DefaultEndpoint = "https://api.fastly.com" +// DefaultAccount is the default Fastly Accounts endpoint. +const DefaultAccount = "https://accounts.fastly.com" + // Data holds global-ish configuration data from all sources: environment // variables, config files, and flags. It has methods to give each parameter to // the components that need it, including the place the parameter came from, @@ -118,11 +121,29 @@ func (d *Data) Endpoint() (string, lookup.Source) { return DefaultEndpoint, lookup.SourceDefault // this method should not fail } +// Account yields the Accounts endpoint. +func (d *Data) Account() (string, lookup.Source) { + if d.Flags.Account != "" { + return d.Flags.Account, lookup.SourceFlag + } + + if d.Env.Account != "" { + return d.Env.Account, lookup.SourceEnvironment + } + + if d.Config.Fastly.AccountEndpoint != DefaultAccount && d.Config.Fastly.AccountEndpoint != "" { + return d.Config.Fastly.AccountEndpoint, lookup.SourceFile + } + + return DefaultAccount, lookup.SourceDefault // this method should not fail +} + // Flags represents all of the configuration parameters that can be set with // explicit flags. Consumers should bind their flag values to these fields // directly. type Flags struct { AcceptDefaults bool + Account string AutoYes bool Debug bool Endpoint string From c0314a79914a195639e1e5abb355f09d25973a7d Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 16:13:33 +0100 Subject: [PATCH 017/115] refactor: move token exchange to separate function --- pkg/app/run.go | 37 +++--------------- pkg/auth/auth.go | 64 +++++++++++++++++-------------- pkg/commands/authenticate/root.go | 3 ++ 3 files changed, 43 insertions(+), 61 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 16084ae5e..fa59100b9 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -1,11 +1,9 @@ package app import ( - "encoding/json" "errors" "fmt" "io" - "net/http" "os" "slices" "strconv" @@ -16,7 +14,6 @@ import ( "github.com/fastly/kingpin" "github.com/fastly/cli/pkg/api" - "github.com/fastly/cli/pkg/api/undocumented" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/commands/update" "github.com/fastly/cli/pkg/commands/version" @@ -146,50 +143,26 @@ func Run(opts RunOpts) error { text.Info(opts.Stdout, "Your access token has now expired. We will attempt to refresh it") } text.Break(opts.Stdout) + account, _ := g.Account() updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) if err != nil { return fmt.Errorf("failed to refresh access token: %w", err) } - claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) if err != nil { return fmt.Errorf("failed to verify refreshed JWT: %w", err) } - email, ok := claims["email"] if !ok { return errors.New("failed to extract email from JWT claims") } - endpoint, _ := g.Endpoint() - - // Exchange the access token for an API token - resp, err := undocumented.Call(undocumented.CallOptions{ - APIEndpoint: endpoint, - HTTPClient: g.HTTPClient, - HTTPHeaders: []undocumented.HTTPHeader{ - { - Key: "Authorization", - Value: fmt.Sprintf("Bearer %s", updated.AccessToken), - }, - }, - Method: http.MethodPost, - Path: "/login-enhanced", - }) - if err != nil { - if apiErr, ok := err.(undocumented.APIError); ok { - if apiErr.StatusCode != http.StatusConflict { - err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) - } - } - return err - } - - at := &auth.APIToken{} - err = json.Unmarshal(resp, at) + // Exchange the access token for a Fastly API token + apiEndpoint, _ := g.Endpoint() + at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) if err != nil { - return fmt.Errorf("failed to unmarshal json containing API token: %w", err) + return fmt.Errorf("failed to exchange access token for an API token: %w", err) } // NOTE: The refresh token can sometimes be refreshed along with the access token. diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 947fce83a..e62f5781e 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -107,42 +107,16 @@ func (s *Server) handleCallback() http.HandlerFunc { return } - // Exchange the access token for an API token - resp, err := undocumented.Call(undocumented.CallOptions{ - APIEndpoint: s.APIEndpoint, - HTTPClient: s.HTTPClient, - HTTPHeaders: []undocumented.HTTPHeader{ - { - Key: "Authorization", - Value: fmt.Sprintf("Bearer %s", j.AccessToken), - }, - }, - Method: http.MethodPost, - Path: "/login-enhanced", - }) + // Exchange the access token for a Fastly API token. + at, err := ExchangeAccessToken(j.AccessToken, s.APIEndpoint, s.HTTPClient) if err != nil { - if apiErr, ok := err.(undocumented.APIError); ok { - if apiErr.StatusCode != http.StatusConflict { - err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) - } - } s.Result <- AuthorizationResult{ - Err: err, - } - return - } - - at := &APIToken{} - err = json.Unmarshal(resp, at) - if err != nil { - s.Result <- AuthorizationResult{ - Err: fmt.Errorf("failed to unmarshal json containing API token: %w", err), + Err: fmt.Errorf("failed to exchange access token for an API token: %w", err), } return } fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") - s.Result <- AuthorizationResult{ Email: email.(string), Jwt: j, @@ -321,3 +295,35 @@ func RefreshAccessToken(accountEndpoint, refreshToken string) (JWT, error) { return j, nil } + +// ExchangeAccessToken exchanges `accessToken` for a Fastly API token. +func ExchangeAccessToken(accessToken, apiEndpoint string, httpClient api.HTTPClient) (*APIToken, error) { + resp, err := undocumented.Call(undocumented.CallOptions{ + APIEndpoint: apiEndpoint, + HTTPClient: httpClient, + HTTPHeaders: []undocumented.HTTPHeader{ + { + Key: "Authorization", + Value: fmt.Sprintf("Bearer %s", accessToken), + }, + }, + Method: http.MethodPost, + Path: "/login-enhanced", + }) + if err != nil { + if apiErr, ok := err.(undocumented.APIError); ok { + if apiErr.StatusCode != http.StatusConflict { + err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) + } + } + return nil, err + } + + at := &APIToken{} + err = json.Unmarshal(resp, at) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal json containing API token: %w", err) + } + + return at, nil +} diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index a455bc3af..7b1b2b0bb 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -108,6 +108,9 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { // If no profiles configured at all... if profileConfigured == "" && profileDefault == "" { now := time.Now().Unix() + if c.Globals.Config.Profiles == nil { + c.Globals.Config.Profiles = make(config.Profiles) + } c.Globals.Config.Profiles[profile.DefaultName] = &config.Profile{ AccessToken: ar.Jwt.AccessToken, AccessTokenCreated: now, From 79946beb4a3a0477b7e7e5be72ce0167040f21ab Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 17:26:12 +0100 Subject: [PATCH 018/115] refactor: move auth logic to separate functions --- pkg/app/run.go | 326 +++++++++++++++++++++++++---------------------- pkg/auth/auth.go | 7 + 2 files changed, 184 insertions(+), 149 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index fa59100b9..a3353dcb8 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -15,6 +15,7 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/auth" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/update" "github.com/fastly/cli/pkg/commands/version" "github.com/fastly/cli/pkg/config" @@ -91,159 +92,19 @@ func Run(opts RunOpts) error { opts.Manifest.File.SetQuiet(true) } - token, tokenSource := g.Token() + // Check the profile token and refresh if expired. authWarningMsg := "No API token could be found" - // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. - if tokenSource == lookup.SourceFile && commandRequiresToken(commandName) { - var ( - profileData *config.Profile - found bool - name, profileName string - ) - switch { - case g.Flags.Profile != "": - profileName = g.Flags.Profile - case g.Manifest.File.Profile != "": - profileName = g.Manifest.File.Profile - default: - profileName = "default" - } - for name, profileData = range g.Config.Profiles { - if (profileName == "default" && profileData.Default) || name == profileName { - // Once we find the default profile we can update the variable to be the - // associated profile name so later on we can use that information to - // update the specific profile. - if profileName == "default" { - profileName = name - } - found = true - break - } - } - if !found { - return fmt.Errorf("failed to locate '%s' profile", profileName) - } - - ttl := time.Duration(profileData.AccessTokenTTL) * time.Second - delta := time.Now().Add(-ttl).Unix() - - // Access Token has expired - if profileData.AccessTokenCreated < delta { - ttl := time.Duration(profileData.RefreshTokenTTL) * time.Second - diff := time.Now().Add(-ttl).Unix() - - if profileData.RefreshTokenCreated < diff { - authWarningMsg = "Your API token has expired and so has your refresh token" - // To re-authenticate we simple reset the tokenSource variable. - // A later conditional block catches it and trigger a re-auth. - tokenSource = lookup.SourceUndefined - } else { - if !g.Flags.Quiet { - text.Info(opts.Stdout, "Your access token has now expired. We will attempt to refresh it") - } - text.Break(opts.Stdout) - - account, _ := g.Account() - updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) - if err != nil { - return fmt.Errorf("failed to refresh access token: %w", err) - } - claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) - if err != nil { - return fmt.Errorf("failed to verify refreshed JWT: %w", err) - } - email, ok := claims["email"] - if !ok { - return errors.New("failed to extract email from JWT claims") - } - - // Exchange the access token for a Fastly API token - apiEndpoint, _ := g.Endpoint() - at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) - if err != nil { - return fmt.Errorf("failed to exchange access token for an API token: %w", err) - } - - // NOTE: The refresh token can sometimes be refreshed along with the access token. - // This happens all the time in my testing but according to what is - // spec'd this apparently is something that _might_ happen. - // So after we get the refreshed access token, we check to see if the - // refresh token that was returned by the API call has also changed when - // compared to the refresh token stored in the CLI config file. - name, current := profile.Get(profileName, g.Config.Profiles) - if name == "" { - return fmt.Errorf("failed to locate '%s' profile", profileName) - } - now := time.Now().Unix() - refreshToken := current.RefreshToken - refreshTokenCreated := current.RefreshTokenCreated - refreshTokenTTL := current.RefreshTokenTTL - if current.RefreshToken != updated.RefreshToken { - if !g.Flags.Quiet { - text.Info(opts.Stdout, "Your refresh token was also updated") - } - refreshToken = updated.RefreshToken - refreshTokenCreated = now - refreshTokenTTL = updated.RefreshExpiresIn - } - - ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { - p.AccessToken = updated.AccessToken - p.AccessTokenCreated = now - p.AccessTokenTTL = updated.ExpiresIn - p.Email = email.(string) - p.RefreshToken = refreshToken - p.RefreshTokenCreated = refreshTokenCreated - p.RefreshTokenTTL = refreshTokenTTL - p.Token = at.AccessToken - }) - if !ok { - return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), - Remediation: "Run `fastly authenticate` to retry.", - } - } - g.Config.Profiles = ps - if err := g.Config.Write(g.ConfigPath); err != nil { - g.ErrLog.Add(err) - return fmt.Errorf("error saving config file: %w", err) - } - } - } + token, tokenSource := g.Token() + tokenSource, authWarningMsg, err = checkProfileToken(tokenSource, commandName, authWarningMsg, opts.Stdout, &g) + if err != nil { + return fmt.Errorf("failed to check profile token: %w", err) } - // Ensure the user has configured an API token, otherwise trigger the - // authentication flow (unless calling a tokenless command). - if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { - for _, command := range commands { - if command.Name() == "authenticate" { - text.Warning(opts.Stdout, "%s. We need to open your browser to authenticate you.", authWarningMsg) - text.Break(opts.Stdout) - cont, err := text.AskYesNo(opts.Stdout, "Are you sure you want to continue? [y/N]: ", opts.Stdin) - if err != nil { - return err - } - if !cont { - return nil - } - text.Break(opts.Stdout) - - err = command.Exec(opts.Stdin, opts.Stdout) - if err != nil { - return fmt.Errorf("failed to authenticate: %w", err) - } - text.Break(opts.Stdout) - - break - } - } - - // Recheck for token (should be persisted to profile data). - token, tokenSource = g.Token() - if tokenSource == lookup.SourceUndefined { - return fsterr.ErrNoToken - } + // If no token, trigger authentication (unless tokenless command) + token, tokenSource, err = authenticateUnlessTokenExists(tokenSource, token, commandName, authWarningMsg, commands, opts.Stdin, opts.Stdout, &g) + if err != nil { + return fmt.Errorf("failed to check profile token: %w", err) } if g.Verbose() { @@ -379,6 +240,173 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { return app } +// checkProfileToken can potentially modify `tokenSource` to trigger a re-auth. +// It can also return a modified `authWarningMsg` depending on user case. +// +// NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. +func checkProfileToken( + tokenSource lookup.Source, + commandName, authWarningMsg string, + out io.Writer, + g *global.Data, +) (lookup.Source, string, error) { + if tokenSource == lookup.SourceFile && commandRequiresToken(commandName) { + var ( + profileData *config.Profile + found bool + name, profileName string + ) + switch { + case g.Flags.Profile != "": + profileName = g.Flags.Profile + case g.Manifest.File.Profile != "": + profileName = g.Manifest.File.Profile + default: + profileName = "default" + } + for name, profileData = range g.Config.Profiles { + if (profileName == "default" && profileData.Default) || name == profileName { + // Once we find the default profile we can update the variable to be the + // associated profile name so later on we can use that information to + // update the specific profile. + if profileName == "default" { + profileName = name + } + found = true + break + } + } + if !found { + return tokenSource, authWarningMsg, fmt.Errorf("failed to locate '%s' profile", profileName) + } + + // Access Token has expired + if auth.TokenExpired(profileData.AccessTokenTTL, profileData.AccessTokenCreated) { + if auth.TokenExpired(profileData.RefreshTokenTTL, profileData.RefreshTokenCreated) { + authWarningMsg = "Your API token has expired and so has your refresh token" + // To re-authenticate we simple reset the tokenSource variable. + // A later conditional block catches it and trigger a re-auth. + tokenSource = lookup.SourceUndefined + } else { + if !g.Flags.Quiet { + text.Info(out, "Your access token has now expired. We will attempt to refresh it") + } + + account, _ := g.Account() + updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) + if err != nil { + return tokenSource, authWarningMsg, fmt.Errorf("failed to refresh access token: %w", err) + } + claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) + if err != nil { + return tokenSource, authWarningMsg, fmt.Errorf("failed to verify refreshed JWT: %w", err) + } + email, ok := claims["email"] + if !ok { + return tokenSource, authWarningMsg, errors.New("failed to extract email from JWT claims") + } + + // Exchange the access token for a Fastly API token + apiEndpoint, _ := g.Endpoint() + at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) + if err != nil { + return tokenSource, authWarningMsg, fmt.Errorf("failed to exchange access token for an API token: %w", err) + } + + // NOTE: The refresh token can sometimes be refreshed along with the access token. + // This happens all the time in my testing but according to what is + // spec'd this apparently is something that _might_ happen. + // So after we get the refreshed access token, we check to see if the + // refresh token that was returned by the API call has also changed when + // compared to the refresh token stored in the CLI config file. + name, current := profile.Get(profileName, g.Config.Profiles) + if name == "" { + return tokenSource, authWarningMsg, fmt.Errorf("failed to locate '%s' profile", profileName) + } + now := time.Now().Unix() + refreshToken := current.RefreshToken + refreshTokenCreated := current.RefreshTokenCreated + refreshTokenTTL := current.RefreshTokenTTL + if current.RefreshToken != updated.RefreshToken { + if !g.Flags.Quiet { + text.Info(out, "Your refresh token was also updated") + text.Break(out) + } + refreshToken = updated.RefreshToken + refreshTokenCreated = now + refreshTokenTTL = updated.RefreshExpiresIn + } + + ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { + p.AccessToken = updated.AccessToken + p.AccessTokenCreated = now + p.AccessTokenTTL = updated.ExpiresIn + p.Email = email.(string) + p.RefreshToken = refreshToken + p.RefreshTokenCreated = refreshTokenCreated + p.RefreshTokenTTL = refreshTokenTTL + p.Token = at.AccessToken + }) + if !ok { + return tokenSource, authWarningMsg, fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), + Remediation: "Run `fastly authenticate` to retry.", + } + } + g.Config.Profiles = ps + if err := g.Config.Write(g.ConfigPath); err != nil { + g.ErrLog.Add(err) + return tokenSource, authWarningMsg, fmt.Errorf("error saving config file: %w", err) + } + } + } + } + + return tokenSource, authWarningMsg, nil +} + +func authenticateUnlessTokenExists( + tokenSource lookup.Source, + token, commandName, authWarningMsg string, + commands []cmd.Command, + in io.Reader, + out io.Writer, + g *global.Data, +) (string, lookup.Source, error) { + if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { + for _, command := range commands { + if command.Name() == "authenticate" { + text.Warning(out, "%s. We need to open your browser to authenticate you.", authWarningMsg) + text.Break(out) + cont, err := text.AskYesNo(out, "Are you sure you want to continue? [y/N]: ", in) + if err != nil { + return token, tokenSource, err + } + if !cont { + return token, tokenSource, nil + } + text.Break(out) + + err = command.Exec(in, out) + if err != nil { + return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) + } + text.Break(out) + + break + } + } + + // Recheck for token (should be persisted to profile data). + token, tokenSource = g.Token() + if tokenSource == lookup.SourceUndefined { + return token, tokenSource, fsterr.ErrNoToken + } + } + + return token, tokenSource, nil +} + // APIClientFactory creates a Fastly API client (modeled as an api.Interface) // from a user-provided API token. It exists as a type in order to parameterize // the Run helper with it: in the real CLI, we can use NewClient from the Fastly diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index e62f5781e..3994347ec 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -327,3 +327,10 @@ func ExchangeAccessToken(accessToken, apiEndpoint string, httpClient api.HTTPCli return at, nil } + +// TokenExpired indicates if the specified TTL has past. +func TokenExpired(ttl int, timestamp int64) bool { + d := time.Duration(ttl) * time.Second + ttlAgo := time.Now().Add(-d).Unix() + return timestamp < ttlAgo +} From 371d4dbe452be334dc69ebba4a5e0a8adca1b1a9 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 17:33:39 +0100 Subject: [PATCH 019/115] fix(config): update config version --- .fastly/config.toml | 1 + pkg/app/run.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.fastly/config.toml b/.fastly/config.toml index 5d5de4c7a..a9648137c 100644 --- a/.fastly/config.toml +++ b/.fastly/config.toml @@ -1,6 +1,7 @@ config_version = 4 [fastly] +account_endpoint = "https://accounts.fastly.com" api_endpoint = "https://api.fastly.com" [wasm-metadata] diff --git a/pkg/app/run.go b/pkg/app/run.go index a3353dcb8..78556d111 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -141,12 +141,12 @@ func Run(opts RunOpts) error { endpoint, endpointSource := g.Endpoint() if g.Verbose() { switch endpointSource { + case lookup.SourceFlag: + fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) case lookup.SourceEnvironment: fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) case lookup.SourceFile: fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via config file): %s\n\n", endpoint) - case lookup.SourceFlag: - fmt.Fprintf(opts.Stdout, "Fastly API endpoint provided via --endpoint\n\n") case lookup.SourceDefault, lookup.SourceUndefined: fallthrough default: From da3c322526a61738d58f2c16959d1582d74a44d1 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 17:46:59 +0100 Subject: [PATCH 020/115] refactor(app): make prompt less scary --- pkg/app/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 78556d111..604ed9276 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -378,7 +378,7 @@ func authenticateUnlessTokenExists( if command.Name() == "authenticate" { text.Warning(out, "%s. We need to open your browser to authenticate you.", authWarningMsg) text.Break(out) - cont, err := text.AskYesNo(out, "Are you sure you want to continue? [y/N]: ", in) + cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) if err != nil { return token, tokenSource, err } From 25e27f89f9df2b3eb3e60ac05301193850ee5f69 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 7 Sep 2023 17:53:43 +0100 Subject: [PATCH 021/115] test: fix whoami test --- pkg/commands/whoami/whoami_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index 658855c7a..d85b73515 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -58,7 +58,7 @@ func TestWhoami(t *testing.T) { client: verifyClient(basicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, "Fastly API endpoint: https://api.fastly.com", - "Fastly API endpoint provided via --endpoint", + "Fastly API endpoint (via --endpoint): https://staging.fastly.com", ), }, { From 691761db22f922c696eaad3af786f166691babcf Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 11:52:18 +0100 Subject: [PATCH 022/115] fix: support skipping OAuth --- pkg/app/run.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 604ed9276..2415581a9 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -96,7 +96,7 @@ func Run(opts RunOpts) error { authWarningMsg := "No API token could be found" token, tokenSource := g.Token() - tokenSource, authWarningMsg, err = checkProfileToken(tokenSource, commandName, authWarningMsg, opts.Stdout, &g) + tokenSource, authWarningMsg, err = checkProfileToken(tokenSource, commandName, authWarningMsg, opts.Stdin, opts.Stdout, &g) if err != nil { return fmt.Errorf("failed to check profile token: %w", err) } @@ -247,6 +247,7 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { func checkProfileToken( tokenSource lookup.Source, commandName, authWarningMsg string, + in io.Reader, out io.Writer, g *global.Data, ) (lookup.Source, string, error) { @@ -280,9 +281,19 @@ func checkProfileToken( return tokenSource, authWarningMsg, fmt.Errorf("failed to locate '%s' profile", profileName) } + // Allow a user to skip OAuth if they prefer long-lived tokens. + if shouldSkipOAuth(profileData, in, out, g) { + return tokenSource, authWarningMsg, nil + } + // Access Token has expired if auth.TokenExpired(profileData.AccessTokenTTL, profileData.AccessTokenCreated) { if auth.TokenExpired(profileData.RefreshTokenTTL, profileData.RefreshTokenCreated) { + // TODO: Tweak warning message if dealing with long-lived token user. + // Specifically, we need to check if they opted to not skip OAuth. + // To do that might need changing the bool type of `SkipOAuth` to a + // pointer so we can check if it is nil rather than false (which is the + // zero value for that type). authWarningMsg = "Your API token has expired and so has your refresh token" // To re-authenticate we simple reset the tokenSource variable. // A later conditional block catches it and trigger a re-auth. @@ -365,6 +376,27 @@ func checkProfileToken( return tokenSource, authWarningMsg, nil } +// shouldSkipOAuth identifies if a config is a pre-v4 config and, if it is, will +// prompt the user to confirm if they want to swap their token for OAuth flow. +// +// If dealing with a long-lived token, we default to saying "yes" to keep it. +func shouldSkipOAuth(pd *config.Profile, in io.Reader, out io.Writer, g *global.Data) bool { + // If user has followed OAuth flow before, then these will not be zero values. + if pd.AccessToken == "" && pd.RefreshToken == "" && pd.AccessTokenCreated == 0 && pd.RefreshTokenCreated == 0 { + if g.Flags.AutoYes || g.Flags.NonInteractive { + return true + } + text.Info(out, "Your token doesn't appear to have been generated using Fastly's OAuth2 account flow (which offers more security as it uses short-lived tokens that can be automatically regenerated for a period of time).") + text.Break(out) + keepToken, err := text.AskYesNo(out, "Do you want to keep your current token? [y/N]: ", in) + if err == nil && keepToken { + return true + } + return false + } + return false +} + func authenticateUnlessTokenExists( tokenSource lookup.Source, token, commandName, authWarningMsg string, From 2ca512477c0c9f12a0e35efbe229bbc29902c034 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 14:01:44 +0100 Subject: [PATCH 023/115] refactor(app): move more logic from Run function --- pkg/app/run.go | 128 +++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 2415581a9..271717b96 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -92,51 +92,18 @@ func Run(opts RunOpts) error { opts.Manifest.File.SetQuiet(true) } - // Check the profile token and refresh if expired. - authWarningMsg := "No API token could be found" - - token, tokenSource := g.Token() - tokenSource, authWarningMsg, err = checkProfileToken(tokenSource, commandName, authWarningMsg, opts.Stdin, opts.Stdout, &g) + token, tokenSource, err := processToken(commands, commandName, env.Token, opts.Manifest.File.Profile, &g, opts.Stdin, opts.Stdout) if err != nil { - return fmt.Errorf("failed to check profile token: %w", err) + return fmt.Errorf("failed to process token: %w", err) } - // If no token, trigger authentication (unless tokenless command) - token, tokenSource, err = authenticateUnlessTokenExists(tokenSource, token, commandName, authWarningMsg, commands, opts.Stdin, opts.Stdout, &g) - if err != nil { - return fmt.Errorf("failed to check profile token: %w", err) - } - - if g.Verbose() { - displayTokenSource( - tokenSource, - opts.Stdout, - env.Token, - determineProfile(opts.Manifest.File.Profile, g.Flags.Profile, g.Config.Profiles), - ) - } - - // Handle any profile override. + // Check for a profile override. token, err = profile.Init(token, opts.Manifest, &g, opts.Stdin, opts.Stdout) if err != nil { return err } - // If we are using the token from config file, check the file's permissions - // to assert if they are not too open or have been altered outside of the - // application and warn if so. - segs := strings.Split(commandName, " ") - if tokenSource == lookup.SourceFile && (len(segs) > 0 && segs[0] != "profile") { - if fi, err := os.Stat(config.FilePath); err == nil { - if mode := fi.Mode().Perm(); mode > config.FilePermissions { - if !g.Flags.Quiet { - text.Warning(opts.Stdout, "Unprotected configuration file.\n\n") - text.Output(opts.Stdout, "Permissions for '%s' are too open\n\n", config.FilePath) - text.Output(opts.Stdout, "It is recommended that your configuration file is NOT accessible by others.\n\n") - } - } - } - } + checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, opts.Stdout) endpoint, endpointSource := g.Endpoint() if g.Verbose() { @@ -240,13 +207,39 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { return app } +// processToken first checks if a profile token is defined in config and if so, +// it will validate if it has expired, and if it has it will attempt to refresh +// it. If both the access token and the refresh token has expired it will +// trigger the `fastly authenticate` command to execute. Either way the CLI +// config is updated to reflect the token that was either refreshed or +// regenerated from the authentication process. +func processToken(commands []cmd.Command, commandName, envToken, profileName string, g *global.Data, in io.Reader, out io.Writer) (token string, tokenSource lookup.Source, err error) { + // Check the profile token and refresh if expired. + warningMessage := "No API token could be found" + token, tokenSource = g.Token() + tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, in, out, g) + if err != nil { + return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) + } + + // If no token, trigger authentication (unless tokenless command) + token, tokenSource, err = authenticateUnlessTokenExists(tokenSource, token, commandName, warningMessage, commands, in, out, g) + if err != nil { + return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) + } + + displayTokenIfVerbose(tokenSource, envToken, profileName, *g, out) + + return token, tokenSource, nil +} + // checkProfileToken can potentially modify `tokenSource` to trigger a re-auth. -// It can also return a modified `authWarningMsg` depending on user case. +// It can also return a modified `warningMessage` depending on user case. // // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. func checkProfileToken( tokenSource lookup.Source, - commandName, authWarningMsg string, + commandName, warningMessage string, in io.Reader, out io.Writer, g *global.Data, @@ -278,12 +271,12 @@ func checkProfileToken( } } if !found { - return tokenSource, authWarningMsg, fmt.Errorf("failed to locate '%s' profile", profileName) + return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) } // Allow a user to skip OAuth if they prefer long-lived tokens. if shouldSkipOAuth(profileData, in, out, g) { - return tokenSource, authWarningMsg, nil + return tokenSource, warningMessage, nil } // Access Token has expired @@ -294,7 +287,7 @@ func checkProfileToken( // To do that might need changing the bool type of `SkipOAuth` to a // pointer so we can check if it is nil rather than false (which is the // zero value for that type). - authWarningMsg = "Your API token has expired and so has your refresh token" + warningMessage = "Your access token has expired and so has your refresh token" // To re-authenticate we simple reset the tokenSource variable. // A later conditional block catches it and trigger a re-auth. tokenSource = lookup.SourceUndefined @@ -306,22 +299,22 @@ func checkProfileToken( account, _ := g.Account() updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) if err != nil { - return tokenSource, authWarningMsg, fmt.Errorf("failed to refresh access token: %w", err) + return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) } claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) if err != nil { - return tokenSource, authWarningMsg, fmt.Errorf("failed to verify refreshed JWT: %w", err) + return tokenSource, warningMessage, fmt.Errorf("failed to verify refreshed JWT: %w", err) } email, ok := claims["email"] if !ok { - return tokenSource, authWarningMsg, errors.New("failed to extract email from JWT claims") + return tokenSource, warningMessage, errors.New("failed to extract email from JWT claims") } // Exchange the access token for a Fastly API token apiEndpoint, _ := g.Endpoint() at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) if err != nil { - return tokenSource, authWarningMsg, fmt.Errorf("failed to exchange access token for an API token: %w", err) + return tokenSource, warningMessage, fmt.Errorf("failed to exchange access token for an API token: %w", err) } // NOTE: The refresh token can sometimes be refreshed along with the access token. @@ -332,7 +325,7 @@ func checkProfileToken( // compared to the refresh token stored in the CLI config file. name, current := profile.Get(profileName, g.Config.Profiles) if name == "" { - return tokenSource, authWarningMsg, fmt.Errorf("failed to locate '%s' profile", profileName) + return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) } now := time.Now().Unix() refreshToken := current.RefreshToken @@ -359,7 +352,7 @@ func checkProfileToken( p.Token = at.AccessToken }) if !ok { - return tokenSource, authWarningMsg, fsterr.RemediationError{ + return tokenSource, warningMessage, fsterr.RemediationError{ Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), Remediation: "Run `fastly authenticate` to retry.", } @@ -367,13 +360,13 @@ func checkProfileToken( g.Config.Profiles = ps if err := g.Config.Write(g.ConfigPath); err != nil { g.ErrLog.Add(err) - return tokenSource, authWarningMsg, fmt.Errorf("error saving config file: %w", err) + return tokenSource, warningMessage, fmt.Errorf("error saving config file: %w", err) } } } } - return tokenSource, authWarningMsg, nil + return tokenSource, warningMessage, nil } // shouldSkipOAuth identifies if a config is a pre-v4 config and, if it is, will @@ -399,7 +392,7 @@ func shouldSkipOAuth(pd *config.Profile, in io.Reader, out io.Writer, g *global. func authenticateUnlessTokenExists( tokenSource lookup.Source, - token, commandName, authWarningMsg string, + token, commandName, warningMessage string, commands []cmd.Command, in io.Reader, out io.Writer, @@ -408,16 +401,16 @@ func authenticateUnlessTokenExists( if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { for _, command := range commands { if command.Name() == "authenticate" { - text.Warning(out, "%s. We need to open your browser to authenticate you.", authWarningMsg) + text.Warning(out, "%s. We need to open your browser to authenticate you.", warningMessage) text.Break(out) cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) + text.Break(out) if err != nil { return token, tokenSource, err } if !cont { return token, tokenSource, nil } - text.Break(out) err = command.Exec(in, out) if err != nil { @@ -439,6 +432,35 @@ func authenticateUnlessTokenExists( return token, tokenSource, nil } +func displayTokenIfVerbose(tokenSource lookup.Source, envToken string, profileName string, g global.Data, out io.Writer) { + if g.Verbose() { + displayTokenSource( + tokenSource, + out, + envToken, + determineProfile(profileName, g.Flags.Profile, g.Config.Profiles), + ) + } +} + +// If we are using the token from config file, check the file's permissions +// to assert if they are not too open or have been altered outside of the +// application and warn if so. +func checkConfigPermissions(quietMode bool, commandName string, tokenSource lookup.Source, out io.Writer) { + segs := strings.Split(commandName, " ") + if tokenSource == lookup.SourceFile && (len(segs) > 0 && segs[0] != "profile") { + if fi, err := os.Stat(config.FilePath); err == nil { + if mode := fi.Mode().Perm(); mode > config.FilePermissions { + if !quietMode { + text.Warning(out, "Unprotected configuration file.\n\n") + text.Output(out, "Permissions for '%s' are too open\n\n", config.FilePath) + text.Output(out, "It is recommended that your configuration file is NOT accessible by others.\n\n") + } + } + } + } +} + // APIClientFactory creates a Fastly API client (modeled as an api.Interface) // from a user-provided API token. It exists as a type in order to parameterize // the Run helper with it: in the real CLI, we can use NewClient from the Fastly From 58c54715ea71c5e99ff7edd99eab393a7d1df8be Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 14:47:13 +0100 Subject: [PATCH 024/115] refactor(app): move more logic to separate functions + support new env var --- pkg/app/run.go | 96 ++++++++++++++++++--------------- pkg/config/config.go | 10 ++-- pkg/env/env.go | 14 +++-- pkg/errors/remediation_error.go | 2 +- pkg/global/global.go | 4 +- 5 files changed, 75 insertions(+), 51 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 271717b96..999859783 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -92,7 +92,7 @@ func Run(opts RunOpts) error { opts.Manifest.File.SetQuiet(true) } - token, tokenSource, err := processToken(commands, commandName, env.Token, opts.Manifest.File.Profile, &g, opts.Stdin, opts.Stdout) + token, tokenSource, err := processToken(commands, commandName, opts.Manifest.File.Profile, &g, opts.Stdin, opts.Stdout) if err != nil { return fmt.Errorf("failed to process token: %w", err) } @@ -106,45 +106,17 @@ func Run(opts RunOpts) error { checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, opts.Stdout) endpoint, endpointSource := g.Endpoint() - if g.Verbose() { - switch endpointSource { - case lookup.SourceFlag: - fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) - case lookup.SourceEnvironment: - fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) - case lookup.SourceFile: - fmt.Fprintf(opts.Stdout, "Fastly API endpoint (via config file): %s\n\n", endpoint) - case lookup.SourceDefault, lookup.SourceUndefined: - fallthrough - default: - fmt.Fprintf(opts.Stdout, "Fastly API endpoint: %s\n\n", endpoint) - } - } - // NOTE: We return error immediately so there's no issue assigning to global. - // nosemgrep - g.APIClient, err = opts.APIClient(token, endpoint, g.Flags.Debug) - if err != nil { - g.ErrLog.Add(err) - return fmt.Errorf("error constructing Fastly API client: %w", err) - } + displayAPIEndpoint(endpoint, endpointSource, g.Verbose(), opts.Stdout) - // NOTE: We return error immediately so there's no issue assigning to global. - // nosemgrep - g.RTSClient, err = fastly.NewRealtimeStatsClientForEndpoint(token, fastly.DefaultRealtimeStatsEndpoint) + g.APIClient, g.RTSClient, err = configureClients(token, endpoint, opts.APIClient, g.Flags.Debug) if err != nil { g.ErrLog.Add(err) - return fmt.Errorf("error constructing Fastly realtime stats client: %w", err) + return fmt.Errorf("error constructing client: %w", err) } - if opts.Versioners.CLI != nil && commandName != "update" && !version.IsPreRelease(revision.AppVersion) { - f := update.CheckAsync( - revision.AppVersion, - opts.Versioners.CLI, - g.Flags.Quiet, - ) - defer f(opts.Stdout) // ...and the printing function second, so we hit the timeout - } + f := checkForUpdates(opts.Versioners.CLI, commandName, g.Flags.Quiet) + defer f(opts.Stdout) return command.Exec(opts.Stdin, opts.Stdout) } @@ -191,7 +163,7 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // A subcommand can't define a flag that is already global. // Kingpin will otherwise trigger a runtime panic 🎉 // Interestingly, short flags can be reused but only across subcommands. - tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.Token) + tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.APIToken) app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&g.Flags.AcceptDefaults) app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&g.Flags.Account) app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&g.Flags.AutoYes) @@ -213,7 +185,7 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // trigger the `fastly authenticate` command to execute. Either way the CLI // config is updated to reflect the token that was either refreshed or // regenerated from the authentication process. -func processToken(commands []cmd.Command, commandName, envToken, profileName string, g *global.Data, in io.Reader, out io.Writer) (token string, tokenSource lookup.Source, err error) { +func processToken(commands []cmd.Command, commandName, profileName string, g *global.Data, in io.Reader, out io.Writer) (token string, tokenSource lookup.Source, err error) { // Check the profile token and refresh if expired. warningMessage := "No API token could be found" token, tokenSource = g.Token() @@ -228,7 +200,7 @@ func processToken(commands []cmd.Command, commandName, envToken, profileName str return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) } - displayTokenIfVerbose(tokenSource, envToken, profileName, *g, out) + displayTokenIfVerbose(tokenSource, profileName, *g, out) return token, tokenSource, nil } @@ -376,12 +348,13 @@ func checkProfileToken( func shouldSkipOAuth(pd *config.Profile, in io.Reader, out io.Writer, g *global.Data) bool { // If user has followed OAuth flow before, then these will not be zero values. if pd.AccessToken == "" && pd.RefreshToken == "" && pd.AccessTokenCreated == 0 && pd.RefreshTokenCreated == 0 { - if g.Flags.AutoYes || g.Flags.NonInteractive { + if g.Flags.AutoYes || g.Flags.NonInteractive || g.Env.AllowStaticToken == "1" { return true } text.Info(out, "Your token doesn't appear to have been generated using Fastly's OAuth2 account flow (which offers more security as it uses short-lived tokens that can be automatically regenerated for a period of time).") text.Break(out) keepToken, err := text.AskYesNo(out, "Do you want to keep your current token? [y/N]: ", in) + text.Break(out) if err == nil && keepToken { return true } @@ -432,12 +405,11 @@ func authenticateUnlessTokenExists( return token, tokenSource, nil } -func displayTokenIfVerbose(tokenSource lookup.Source, envToken string, profileName string, g global.Data, out io.Writer) { +func displayTokenIfVerbose(tokenSource lookup.Source, profileName string, g global.Data, out io.Writer) { if g.Verbose() { displayTokenSource( tokenSource, out, - envToken, determineProfile(profileName, g.Flags.Profile, g.Config.Profiles), ) } @@ -461,6 +433,46 @@ func checkConfigPermissions(quietMode bool, commandName string, tokenSource look } } +func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, verboseMode bool, out io.Writer) { + if verboseMode { + switch endpointSource { + case lookup.SourceFlag: + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) + case lookup.SourceEnvironment: + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) + case lookup.SourceFile: + fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) + case lookup.SourceDefault, lookup.SourceUndefined: + fallthrough + default: + fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) + } + } +} + +func configureClients(token, endpoint string, acf APIClientFactory, debugMode bool) (apiClient api.Interface, rtsClient api.RealtimeStatsInterface, err error) { + apiClient, err = acf(token, endpoint, debugMode) + if err != nil { + return apiClient, rtsClient, fmt.Errorf("error constructing Fastly API client: %w", err) + } + + rtsClient, err = fastly.NewRealtimeStatsClientForEndpoint(token, fastly.DefaultRealtimeStatsEndpoint) + if err != nil { + return apiClient, rtsClient, fmt.Errorf("error constructing Fastly realtime stats client: %w", err) + } + + return apiClient, rtsClient, nil +} + +func checkForUpdates(av github.AssetVersioner, commandName string, quietMode bool) func(io.Writer) { + if av != nil && commandName != "update" && !version.IsPreRelease(revision.AppVersion) { + return update.CheckAsync(revision.AppVersion, av, quietMode) + } + return func(_ io.Writer) { + // no-op + } +} + // APIClientFactory creates a Fastly API client (modeled as an api.Interface) // from a user-provided API token. It exists as a type in order to parameterize // the Run helper with it: in the real CLI, we can use NewClient from the Fastly @@ -476,12 +488,12 @@ type Versioners struct { } // displayTokenSource prints the token source. -func displayTokenSource(source lookup.Source, out io.Writer, token, profileSource string) { +func displayTokenSource(source lookup.Source, out io.Writer, profileSource string) { switch source { case lookup.SourceFlag: fmt.Fprintf(out, "Fastly API token provided via --token\n") case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API token provided via %s\n", token) + fmt.Fprintf(out, "Fastly API token provided via %s\n", env.APIToken) case lookup.SourceFile: fmt.Fprintf(out, "Fastly API token provided via config file (profile: %s)\n", profileSource) case lookup.SourceDefault, lookup.SourceUndefined: diff --git a/pkg/config/config.go b/pkg/config/config.go index 953fcf0e7..7cd4b2583 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -442,8 +442,11 @@ func (f *File) Write(path string) error { type Environment struct { // Account is the env var we look in for the Accounts endpoint. Account string - // Token is the env var we look in for the Fastly API token. - Token string + // AllowStaticToken enables a user to avoid a CLI interactive prompt regarding + // their token not being generated via the Fastly OAuth flow. + AllowStaticToken string + // APIToken is the env var we look in for the Fastly API token. + APIToken string // Endpoint is the env var we look in for the API endpoint. Endpoint string // WasmMetadataDisable is the env var we look in to disable all data @@ -454,9 +457,10 @@ type Environment struct { // Read populates the fields from the provided environment. func (e *Environment) Read(state map[string]string) { + e.APIToken = state[env.APIToken] e.Account = state[env.Account] + e.AllowStaticToken = state[env.AllowStaticToken] e.Endpoint = state[env.Endpoint] - e.Token = state[env.Token] e.WasmMetadataDisable = state[env.WasmMetadataDisable] } diff --git a/pkg/env/env.go b/pkg/env/env.go index cdc86498c..85f8ff1a1 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -9,12 +9,20 @@ import ( ) const ( - // Token is the env var we look in for the Fastly API token. + // APIToken is the env var we look in for the Fastly API token. // gosec flagged this: // G101 (CWE-798): Potential hardcoded credentials // Disabling as we use the value in the command help output. - /* #nosec */ - Token = "FASTLY_API_TOKEN" + // #nosec + APIToken = "FASTLY_API_TOKEN" + + // AllowStaticToken enables a user to avoid a CLI interactive prompt regarding + // their token not being generated via the Fastly OAuth flow. + // gosec flagged this: + // G101 (CWE-798): Potential hardcoded credentials + // Disabling as this isn't an API token but a boolean 1/0 (on/off). + // #nosec + AllowStaticToken = "FASTLY_ALLOW_STATIC_TOKEN" // Endpoint is the env var we look in for the API endpoint. Endpoint = "FASTLY_API_ENDPOINT" diff --git a/pkg/errors/remediation_error.go b/pkg/errors/remediation_error.go index b6b0aa163..008e6e325 100644 --- a/pkg/errors/remediation_error.go +++ b/pkg/errors/remediation_error.go @@ -57,7 +57,7 @@ var AuthRemediation = fmt.Sprintf(strings.Join([]string{ "Check that you're supplying a valid token, either via --token,", "through the environment variable %s, or through the config file via `fastly profile`.", "Verify that the token is still valid via `fastly whoami`.", -}, " "), env.Token) +}, " "), env.APIToken) // NetworkRemediation suggests, somewhat unhelpfully, to try again later. var NetworkRemediation = strings.Join([]string{ diff --git a/pkg/global/global.go b/pkg/global/global.go index 419251b83..2cf193488 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -70,8 +70,8 @@ func (d *Data) Token() (string, lookup.Source) { return d.Flags.Token, lookup.SourceFlag } - if d.Env.Token != "" { - return d.Env.Token, lookup.SourceEnvironment + if d.Env.APIToken != "" { + return d.Env.APIToken, lookup.SourceEnvironment } if d.Flags.Profile != "" { From 43d4e9ce205da82dac7bab3bb620266c2b215fac Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 15:42:28 +0100 Subject: [PATCH 025/115] fix(profile/create): support OAuth token flow --- pkg/app/commands.go | 2 +- pkg/commands/authenticate/root.go | 3 ++- pkg/commands/profile/create.go | 42 ++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/pkg/app/commands.go b/pkg/app/commands.go index abb93f9d8..037b645f8 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -335,7 +335,7 @@ func defineCommands( popCmdRoot := pop.NewRootCommand(app, g) productsCmdRoot := products.NewRootCommand(app, g, m) profileCmdRoot := profile.NewRootCommand(app, g) - profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g) + profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, authenticateCmdRoot) profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, g) profileList := profile.NewListCommand(profileCmdRoot.CmdClause, g) profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, g) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 7b1b2b0bb..d319a88c8 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -105,7 +105,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { profileDefault, _ := profile.Default(c.Globals.Config.Profiles) - // If no profiles configured at all... + // If no profiles configured at all, create a new default... if profileConfigured == "" && profileDefault == "" { now := time.Now().Unix() if c.Globals.Config.Profiles == nil { @@ -123,6 +123,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { Token: ar.SessionToken, } } else { + // Otherwise, edit the default to have the newly generated tokens. profileName := profileDefault if profileConfigured != "" { profileName = profileConfigured diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 5eb1320b4..5af109b26 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/cli/pkg/commands/authenticate" "github.com/fastly/cli/pkg/config" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" @@ -23,6 +24,7 @@ import ( // CreateCommand represents a Kingpin command. type CreateCommand struct { cmd.Base + authCmd *authenticate.RootCommand automationToken bool clientFactory APIClientFactory @@ -30,9 +32,10 @@ type CreateCommand struct { } // NewCreateCommand returns a new command registered in the parent. -func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *authenticate.RootCommand) *CreateCommand { var c CreateCommand c.Globals = g + c.authCmd = authCmd c.CmdClause = parent.Command("create", "Create user profile") c.CmdClause.Arg("profile", "Profile to create (default 'user')").Default(profile.DefaultName).Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) @@ -43,7 +46,10 @@ func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data // Exec implements the command interface. func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { if profile.Exist(c.profile, c.Globals.Config.Profiles) { - return fmt.Errorf("profile '%s' already exists", c.profile) + return fsterr.RemediationError{ + Inner: fmt.Errorf("profile '%s' already exists", c.profile), + Remediation: "Re-run the command and pass a different value for the 'profile' argument.", + } } // The Default status of a new profile should always be true unless there is @@ -58,9 +64,33 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { } } - if err := c.tokenFlow(def, in, out); err != nil { - return err + // Prompt user to decide on which token flow to take (OAuth or Static) + // Static being a traditional long-lived user/automation token. + // + // Opting for the OAuth flow will generate a short-lived token. + // Otherwise user has to create a token manually, then paste it when prompted. + useOAuthFlow := true + if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { + text.Info(out, "You can create a profile in one of two ways. Either paste in an already long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed.") + text.Break(out) + useOAuthFlow, err = text.AskYesNo(out, "Continue to generate a short-lived token? [y/N]: ", in) + text.Break(out) + if err != nil { + return err + } } + if useOAuthFlow { + err = c.authCmd.Exec(in, out) + if err != nil { + return fmt.Errorf("failed to authenticate: %w", err) + } + text.Break(out) + } else { + if err := c.staticTokenFlow(def, in, out); err != nil { + return err + } + } + if err := c.persistCfg(); err != nil { return err } @@ -70,8 +100,8 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { return nil } -// tokenFlow initialises the token flow. -func (c *CreateCommand) tokenFlow(def bool, in io.Reader, out io.Writer) error { +// staticTokenFlow initialises the token flow for a non-OAuth token. +func (c *CreateCommand) staticTokenFlow(def bool, in io.Reader, out io.Writer) error { token, err := promptForToken(in, out, c.Globals.ErrLog) if err != nil { return err From 2356009b5d5112ba3ab5d31cd65d497d5f77f097 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 15:53:54 +0100 Subject: [PATCH 026/115] refactor(authenticate): move new default logic to separate function --- pkg/commands/authenticate/root.go | 37 ++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index d319a88c8..9e72b0c4f 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -107,21 +107,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { // If no profiles configured at all, create a new default... if profileConfigured == "" && profileDefault == "" { - now := time.Now().Unix() - if c.Globals.Config.Profiles == nil { - c.Globals.Config.Profiles = make(config.Profiles) - } - c.Globals.Config.Profiles[profile.DefaultName] = &config.Profile{ - AccessToken: ar.Jwt.AccessToken, - AccessTokenCreated: now, - AccessTokenTTL: ar.Jwt.ExpiresIn, - Default: true, - Email: ar.Email, - RefreshToken: ar.Jwt.RefreshToken, - RefreshTokenCreated: now, - RefreshTokenTTL: ar.Jwt.RefreshExpiresIn, - Token: ar.SessionToken, - } + c.Globals.Config.Profiles = createNewDefaultProfile(c.Globals.Config.Profiles, ar) } else { // Otherwise, edit the default to have the newly generated tokens. profileName := profileDefault @@ -156,3 +142,24 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { text.Success(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) return nil } + +// IMPORTANT: Mutates the config.Profiles map type. +// We need to return the modified type so it can be safely reassigned. +func createNewDefaultProfile(p config.Profiles, ar auth.AuthorizationResult) config.Profiles { + now := time.Now().Unix() + if p == nil { + p = make(config.Profiles) + } + p[profile.DefaultName] = &config.Profile{ + AccessToken: ar.Jwt.AccessToken, + AccessTokenCreated: now, + AccessTokenTTL: ar.Jwt.ExpiresIn, + Default: true, + Email: ar.Email, + RefreshToken: ar.Jwt.RefreshToken, + RefreshTokenCreated: now, + RefreshTokenTTL: ar.Jwt.RefreshExpiresIn, + Token: ar.SessionToken, + } + return p +} From abf1216802f2b3f1d039f09a896f6b72715b2ac3 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 17:08:24 +0100 Subject: [PATCH 027/115] fix: allow profile override for authenticate command + skip auth prompt --- pkg/app/run.go | 1 + pkg/commands/authenticate/root.go | 75 ++++++++++++++++++++----------- pkg/global/global.go | 4 ++ 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 999859783..af966eb7e 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -385,6 +385,7 @@ func authenticateUnlessTokenExists( return token, tokenSource, nil } + g.SkipAuthPrompt = true // skip the same prompt in `authenticate` command flow err = command.Exec(in, out) if err != nil { return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 9e72b0c4f..0c846ed0d 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -33,7 +33,20 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { } // Exec implements the command interface. -func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { +func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { + if !c.Globals.SkipAuthPrompt { + text.Warning(out, "We need to open your browser to authenticate you.") + text.Break(out) + cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) + text.Break(out) + if err != nil { + return err + } + if !cont { + return fmt.Errorf("user cancelled execution") + } + } + verifier, err := oidc.NewCodeVerifier() if err != nil { return fsterr.RemediationError{ @@ -95,41 +108,24 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { } } - var profileConfigured string + var profileOverride string switch { case c.Globals.Flags.Profile != "": - profileConfigured = c.Globals.Flags.Profile + profileOverride = c.Globals.Flags.Profile case c.Globals.Manifest.File.Profile != "": - profileConfigured = c.Globals.Manifest.File.Profile + profileOverride = c.Globals.Manifest.File.Profile } profileDefault, _ := profile.Default(c.Globals.Config.Profiles) // If no profiles configured at all, create a new default... - if profileConfigured == "" && profileDefault == "" { + if profileOverride == "" && profileDefault == "" { c.Globals.Config.Profiles = createNewDefaultProfile(c.Globals.Config.Profiles, ar) } else { - // Otherwise, edit the default to have the newly generated tokens. - profileName := profileDefault - if profileConfigured != "" { - profileName = profileConfigured - } - ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, func(p *config.Profile) { - now := time.Now().Unix() - p.AccessToken = ar.Jwt.AccessToken - p.AccessTokenCreated = now - p.AccessTokenTTL = ar.Jwt.ExpiresIn - p.Email = ar.Email - p.RefreshToken = ar.Jwt.RefreshToken - p.RefreshTokenCreated = now - p.RefreshTokenTTL = ar.Jwt.RefreshExpiresIn - p.Token = ar.SessionToken - }) - if !ok { - return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update default profile with new token data"), - Remediation: "Run `fastly authenticate` to retry.", - } + // Otherwise, edit the existing profile to have the newly generated tokens. + ps, err := editProfile(profileDefault, profileOverride, c.Globals.Config.Profiles, ar) + if err != nil { + return err } c.Globals.Config.Profiles = ps } @@ -163,3 +159,30 @@ func createNewDefaultProfile(p config.Profiles, ar auth.AuthorizationResult) con } return p } + +// IMPORTANT: Mutates the config.Profiles map type. +// We need to return the modified type so it can be safely reassigned. +func editProfile(profileDefault, profileOverride string, p config.Profiles, ar auth.AuthorizationResult) (config.Profiles, error) { + profileName := profileDefault + if profileOverride != "" { + profileName = profileOverride + } + ps, ok := profile.Edit(profileName, p, func(p *config.Profile) { + now := time.Now().Unix() + p.AccessToken = ar.Jwt.AccessToken + p.AccessTokenCreated = now + p.AccessTokenTTL = ar.Jwt.ExpiresIn + p.Email = ar.Email + p.RefreshToken = ar.Jwt.RefreshToken + p.RefreshTokenCreated = now + p.RefreshTokenTTL = ar.Jwt.RefreshExpiresIn + p.Token = ar.SessionToken + }) + if !ok { + return ps, fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), + Remediation: "Run `fastly authenticate` to retry.", + } + } + return ps, nil +} diff --git a/pkg/global/global.go b/pkg/global/global.go index 2cf193488..246d9d8da 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -44,6 +44,10 @@ type Data struct { Manifest manifest.Data // Output is the output for displaying information (typically os.Stdout) Output io.Writer + // SkipAuthPrompt is used to indicate to the `authenticate` command that the + // interactive prompt can be skipped. This is for scenarios where the command + // is executed directly by the user. + SkipAuthPrompt bool // Custom interfaces From b9f7bd4afd0c9ef9963123a47d7ff160a2a02664 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 17:24:48 +0100 Subject: [PATCH 028/115] fix: allow profile override for profile update command --- pkg/app/commands.go | 2 +- pkg/commands/profile/update.go | 31 +++++++++++++++++++------------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/pkg/app/commands.go b/pkg/app/commands.go index 037b645f8..1d82bf4cc 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -340,7 +340,7 @@ func defineCommands( profileList := profile.NewListCommand(profileCmdRoot.CmdClause, g) profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, g) profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, g) - profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g) + profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, authenticateCmdRoot) purgeCmdRoot := purge.NewRootCommand(app, g, m) rateLimitCmdRoot := ratelimit.NewRootCommand(app, g) rateLimitCreate := ratelimit.NewCreateCommand(rateLimitCmdRoot.CmdClause, g, m) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index ac6cb990d..c0a54374e 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/cli/pkg/commands/authenticate" "github.com/fastly/cli/pkg/config" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" @@ -24,6 +25,7 @@ type APIClientFactory func(token, endpoint string, debugMode bool) (api.Interfac // UpdateCommand represents a Kingpin command. type UpdateCommand struct { cmd.Base + authCmd *authenticate.RootCommand automationToken bool clientFactory APIClientFactory @@ -31,9 +33,10 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *authenticate.RootCommand) *UpdateCommand { var c UpdateCommand c.Globals = g + c.authCmd = authCmd c.CmdClause = parent.Command("update", "Update user profile") c.CmdClause.Arg("profile", "Profile to update (defaults to the currently active profile)").Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) @@ -44,21 +47,25 @@ func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { var ( - name string - p *config.Profile + profileName string + p *config.Profile ) - if c.profile == "" { - name, p = profile.Default(c.Globals.Config.Profiles) - if name == "" { + if c.profile == "" && c.Globals.Flags.Profile == "" { + profileName, p = profile.Default(c.Globals.Config.Profiles) + if profileName == "" { return fsterr.RemediationError{ Inner: fmt.Errorf("no active profile"), Remediation: profile.NoDefaults, } } } else { - name, p = profile.Get(c.profile, c.Globals.Config.Profiles) - if name == "" { + profileName = c.profile + if c.Globals.Flags.Profile != "" { + profileName = c.Globals.Flags.Profile + } + profileName, p = profile.Get(profileName, c.Globals.Config.Profiles) + if profileName == "" { msg := fmt.Sprintf(profile.DoesNotExist, c.profile) return fsterr.RemediationError{ Inner: fmt.Errorf(msg), @@ -67,7 +74,7 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { } } - text.Info(out, "Profile being updated: '%s'.\n\n", name) + text.Info(out, "Profile being updated: '%s'.\n\n", profileName) opts := []profile.EditOption{} @@ -122,9 +129,9 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { var ok bool - ps, ok := profile.Edit(name, c.Globals.Config.Profiles, opts...) + ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, opts...) if !ok { - msg := fmt.Sprintf(profile.DoesNotExist, name) + msg := fmt.Sprintf(profile.DoesNotExist, profileName) return fsterr.RemediationError{ Inner: fmt.Errorf(msg), Remediation: fsterr.ProfileRemediation, @@ -150,7 +157,7 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { return fmt.Errorf("error saving config file: %w", err) } - text.Success(out, "\nProfile '%s' updated", name) + text.Success(out, "\nProfile '%s' updated", profileName) return nil } From 07d43e3cf6b4dfcbb4d602bf1553075c78934a48 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 17:36:54 +0100 Subject: [PATCH 029/115] refactor(profile/update): move static token flow to separate function --- pkg/commands/profile/update.go | 107 ++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index c0a54374e..aacc100b5 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -76,59 +76,12 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { text.Info(out, "Profile being updated: '%s'.\n\n", profileName) - opts := []profile.EditOption{} - - token, err := text.InputSecure(out, text.Prompt("Profile token: (leave blank to skip): "), in) + makeDefault, opts, err := c.staticTokenFlow(p, in, out) if err != nil { - c.Globals.ErrLog.Add(err) - return err - } - if token != "" { - opts = append(opts, func(p *config.Profile) { - p.Token = token - }) + return fmt.Errorf("failed to process the static token flow: %w", err) } - text.Break(out) - text.Break(out) - - makeDefault, err := text.AskYesNo(out, "Make profile the default? [y/N] ", in) - if err != nil { - return err - } - opts = append(opts, func(p *config.Profile) { - p.Default = makeDefault - }) - - // User didn't want to change their token value so reassign original. - if token == "" { - token = p.Token - } - - text.Break(out) - - spinner, err := text.NewSpinner(out) - if err != nil { - return err - } - defer func() { - if err != nil { - c.Globals.ErrLog.Add(err) - } - }() - - endpoint, _ := c.Globals.Endpoint() - - email, err := c.validateToken(token, endpoint, spinner) - if err != nil { - return err - } - opts = append(opts, func(p *config.Profile) { - p.Email = email - }) - var ok bool - ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, opts...) if !ok { msg := fmt.Sprintf(profile.DoesNotExist, profileName) @@ -212,3 +165,59 @@ func (c *UpdateCommand) validateToken(token, endpoint string, spinner text.Spinn } return user.Login, nil } + +func (c *UpdateCommand) staticTokenFlow(p *config.Profile, in io.Reader, out io.Writer) (bool, []profile.EditOption, error) { + var makeDefault bool + opts := []profile.EditOption{} + + token, err := text.InputSecure(out, text.BoldYellow("Profile token: (leave blank to skip): "), in) + if err != nil { + c.Globals.ErrLog.Add(err) + return makeDefault, opts, err + } + if token != "" { + opts = append(opts, func(p *config.Profile) { + p.Token = token + }) + } + + text.Break(out) + text.Break(out) + + makeDefault, err = text.AskYesNo(out, "Make profile the default? [y/N] ", in) + if err != nil { + return makeDefault, opts, err + } + opts = append(opts, func(p *config.Profile) { + p.Default = makeDefault + }) + + // User didn't want to change their token value so reassign original. + if token == "" { + token = p.Token + } + + text.Break(out) + + spinner, err := text.NewSpinner(out) + if err != nil { + return makeDefault, opts, err + } + defer func() { + if err != nil { + c.Globals.ErrLog.Add(err) + } + }() + + endpoint, _ := c.Globals.Endpoint() + + email, err := c.validateToken(token, endpoint, spinner) + if err != nil { + return makeDefault, opts, err + } + opts = append(opts, func(p *config.Profile) { + p.Email = email + }) + + return makeDefault, opts, nil +} From 7d23c8b9d6b88794301aa33216be2ce2543dcbe0 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 8 Sep 2023 18:11:08 +0100 Subject: [PATCH 030/115] fix(profile/update): support OAuth flow --- pkg/commands/profile/update.go | 85 ++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index aacc100b5..75c4bf3b4 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -76,35 +76,39 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { text.Info(out, "Profile being updated: '%s'.\n\n", profileName) - makeDefault, opts, err := c.staticTokenFlow(p, in, out) - if err != nil { - return fmt.Errorf("failed to process the static token flow: %w", err) - } - - var ok bool - ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, opts...) - if !ok { - msg := fmt.Sprintf(profile.DoesNotExist, profileName) - return fsterr.RemediationError{ - Inner: fmt.Errorf(msg), - Remediation: fsterr.ProfileRemediation, + // Prompt user to decide on which token flow to take (OAuth or Static) + // Static being a traditional long-lived user/automation token. + // + // Opting for the OAuth flow will generate a short-lived token. + // Otherwise user has to create a token manually, then paste it when prompted. + useOAuthFlow := true + if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { + text.Info(out, "When updating a profile you can either paste in an already long-lived token or allow the Fastly CLI to regenerate a short-lived token that can be automatically refreshed.") + text.Break(out) + var err error + useOAuthFlow, err = text.AskYesNo(out, "Continue with regenerating a short-lived token? [y/N]: ", in) + text.Break(out) + if err != nil { + return err } } + if useOAuthFlow { + // IMPORTANT: We need to temporarily set profile override. + // This is so the `authenticate` command will use the override, rather than + // incorrectly updating the 'default' profile. + c.Globals.Flags.Profile = profileName - if makeDefault { - // We call SetDefault for its side effect of resetting all other profiles to have - // their Default field set to false. - ps, ok = profile.SetDefault(c.profile, ps) - if !ok { - msg := fmt.Sprintf(profile.DoesNotExist, c.profile) - err := errors.New(msg) - c.Globals.ErrLog.Add(err) - return err + err := c.authCmd.Exec(in, out) + if err != nil { + return fmt.Errorf("failed to authenticate: %w", err) + } + text.Break(out) + } else { + if err := c.staticTokenFlow(profileName, p, in, out); err != nil { + return fmt.Errorf("failed to process the static token flow: %w", err) } } - c.Globals.Config.Profiles = ps - if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { c.Globals.ErrLog.Add(err) return fmt.Errorf("error saving config file: %w", err) @@ -166,14 +170,14 @@ func (c *UpdateCommand) validateToken(token, endpoint string, spinner text.Spinn return user.Login, nil } -func (c *UpdateCommand) staticTokenFlow(p *config.Profile, in io.Reader, out io.Writer) (bool, []profile.EditOption, error) { +func (c *UpdateCommand) staticTokenFlow(profileName string, p *config.Profile, in io.Reader, out io.Writer) error { var makeDefault bool opts := []profile.EditOption{} token, err := text.InputSecure(out, text.BoldYellow("Profile token: (leave blank to skip): "), in) if err != nil { c.Globals.ErrLog.Add(err) - return makeDefault, opts, err + return err } if token != "" { opts = append(opts, func(p *config.Profile) { @@ -186,7 +190,7 @@ func (c *UpdateCommand) staticTokenFlow(p *config.Profile, in io.Reader, out io. makeDefault, err = text.AskYesNo(out, "Make profile the default? [y/N] ", in) if err != nil { - return makeDefault, opts, err + return err } opts = append(opts, func(p *config.Profile) { p.Default = makeDefault @@ -201,7 +205,7 @@ func (c *UpdateCommand) staticTokenFlow(p *config.Profile, in io.Reader, out io. spinner, err := text.NewSpinner(out) if err != nil { - return makeDefault, opts, err + return err } defer func() { if err != nil { @@ -213,11 +217,34 @@ func (c *UpdateCommand) staticTokenFlow(p *config.Profile, in io.Reader, out io. email, err := c.validateToken(token, endpoint, spinner) if err != nil { - return makeDefault, opts, err + return err } opts = append(opts, func(p *config.Profile) { p.Email = email }) - return makeDefault, opts, nil + ps, ok := profile.Edit(profileName, c.Globals.Config.Profiles, opts...) + if !ok { + msg := fmt.Sprintf(profile.DoesNotExist, profileName) + return fsterr.RemediationError{ + Inner: fmt.Errorf(msg), + Remediation: fsterr.ProfileRemediation, + } + } + c.Globals.Config.Profiles = ps + + if makeDefault { + // We call SetDefault for its side effect of resetting all other profiles to have + // their Default field set to false. + ps, ok = profile.SetDefault(profileName, ps) + if !ok { + msg := fmt.Sprintf(profile.DoesNotExist, c.profile) + err := errors.New(msg) + c.Globals.ErrLog.Add(err) + return err + } + } + c.Globals.Config.Profiles = ps + + return nil } From 530364501a8f17761575a81cc7b0b6965028bfb4 Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 10:25:59 +0100 Subject: [PATCH 031/115] fix(app): move endpoint display before token validation --- pkg/app/run.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index af966eb7e..2f1284450 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -92,6 +92,10 @@ func Run(opts RunOpts) error { opts.Manifest.File.SetQuiet(true) } + endpoint, endpointSource := g.Endpoint() + + displayAPIEndpoint(endpoint, endpointSource, g.Verbose(), opts.Stdout) + token, tokenSource, err := processToken(commands, commandName, opts.Manifest.File.Profile, &g, opts.Stdin, opts.Stdout) if err != nil { return fmt.Errorf("failed to process token: %w", err) @@ -105,10 +109,6 @@ func Run(opts RunOpts) error { checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, opts.Stdout) - endpoint, endpointSource := g.Endpoint() - - displayAPIEndpoint(endpoint, endpointSource, g.Verbose(), opts.Stdout) - g.APIClient, g.RTSClient, err = configureClients(token, endpoint, opts.APIClient, g.Flags.Debug) if err != nil { g.ErrLog.Add(err) @@ -438,15 +438,15 @@ func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, verboseMo if verboseMode { switch endpointSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n", endpoint) case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.Endpoint, endpoint) case lookup.SourceFile: fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) case lookup.SourceDefault, lookup.SourceUndefined: fallthrough default: - fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint: %s\n", endpoint) } } } From ec13571cdcab4a8384fe5c730f358369c1ad56b6 Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 14:41:25 +0100 Subject: [PATCH 032/115] fix: avoid breaking change by switching SSO to be opt-in --- pkg/app/run.go | 244 +++++++++--------- pkg/commands/acl/acl_test.go | 2 +- pkg/commands/aclentry/aclentry_test.go | 4 +- pkg/commands/authtoken/authtoken_test.go | 4 +- pkg/commands/backend/backend_test.go | 2 +- pkg/commands/dictionary/dictionary_test.go | 4 +- pkg/commands/domain/domain_test.go | 2 +- pkg/commands/healthcheck/healthcheck_test.go | 2 +- .../azureblob/azureblob_integration_test.go | 2 +- .../bigquery/bigquery_integration_test.go | 2 +- .../cloudfiles/cloudfiles_integration_test.go | 2 +- .../datadog/datadog_integration_test.go | 2 +- .../digitalocean_integration_test.go | 2 +- .../elasticsearch_integration_test.go | 2 +- .../logging/ftp/ftp_integration_test.go | 2 +- .../logging/gcs/gcs_integration_test.go | 2 +- .../googlepubsub_integration_test.go | 2 +- .../logging/heroku/heroku_integration_test.go | 2 +- .../honeycomb/honeycomb_integration_test.go | 2 +- .../logging/https/https_integration_test.go | 2 +- .../logging/kafka/kafka_integration_test.go | 2 +- .../kinesis/kinesis_integration_test.go | 2 +- .../logging/loggly/loggly_integration_test.go | 2 +- .../logshuttle/logshuttle_integration_test.go | 2 +- .../logging/newrelic/newrelic_test.go | 2 +- .../logging/newrelicotlp/newrelicotlp_test.go | 2 +- .../openstack/openstack_integration_test.go | 2 +- .../papertrail/papertrail_integration_test.go | 2 +- .../logging/s3/s3_integration_test.go | 2 +- .../logging/scalyr/scalyr_integration_test.go | 2 +- .../logging/sftp/sftp_integration_test.go | 2 +- .../logging/splunk/splunk_integration_test.go | 2 +- .../sumologic/sumologic_integration_test.go | 2 +- .../logging/syslog/syslog_integration_test.go | 2 +- pkg/commands/profile/profile_test.go | 1 + pkg/commands/profile/update.go | 4 +- pkg/commands/service/service_test.go | 6 +- pkg/commands/serviceauth/service_test.go | 2 +- .../serviceversion/serviceversion_test.go | 2 +- .../custom/certificate/certificate_test.go | 2 +- pkg/commands/user/user_test.go | 4 +- pkg/commands/vcl/custom/custom_test.go | 2 +- pkg/commands/vcl/snippet/snippet_test.go | 2 +- pkg/commands/whoami/whoami_test.go | 2 +- pkg/config/config.go | 9 +- pkg/env/env.go | 12 +- pkg/global/global.go | 5 + 47 files changed, 192 insertions(+), 177 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 2f1284450..677a9d229 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -94,7 +94,9 @@ func Run(opts RunOpts) error { endpoint, endpointSource := g.Endpoint() - displayAPIEndpoint(endpoint, endpointSource, g.Verbose(), opts.Stdout) + if g.Verbose() { + displayAPIEndpoint(endpoint, endpointSource, opts.Stdout) + } token, tokenSource, err := processToken(commands, commandName, opts.Manifest.File.Profile, &g, opts.Stdin, opts.Stdout) if err != nil { @@ -186,10 +188,11 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // config is updated to reflect the token that was either refreshed or // regenerated from the authentication process. func processToken(commands []cmd.Command, commandName, profileName string, g *global.Data, in io.Reader, out io.Writer) (token string, tokenSource lookup.Source, err error) { - // Check the profile token and refresh if expired. warningMessage := "No API token could be found" token, tokenSource = g.Token() - tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, in, out, g) + + // Check if token is from fastly.toml [profile] and refresh if expired. + tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, out, g) if err != nil { return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) } @@ -200,7 +203,9 @@ func processToken(commands []cmd.Command, commandName, profileName string, g *gl return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) } - displayTokenIfVerbose(tokenSource, profileName, *g, out) + if g.Verbose() { + displayToken(tokenSource, profileName, *g, out) + } return token, tokenSource, nil } @@ -212,7 +217,6 @@ func processToken(commands []cmd.Command, commandName, profileName string, g *gl func checkProfileToken( tokenSource lookup.Source, commandName, warningMessage string, - in io.Reader, out io.Writer, g *global.Data, ) (lookup.Source, string, error) { @@ -223,9 +227,9 @@ func checkProfileToken( name, profileName string ) switch { - case g.Flags.Profile != "": + case g.Flags.Profile != "": // --profile profileName = g.Flags.Profile - case g.Manifest.File.Profile != "": + case g.Manifest.File.Profile != "": // `profile` field in fastly.toml profileName = g.Manifest.File.Profile default: profileName = "default" @@ -246,121 +250,129 @@ func checkProfileToken( return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) } - // Allow a user to skip OAuth if they prefer long-lived tokens. - if shouldSkipOAuth(profileData, in, out, g) { + // Allow user to opt-in to OAuth so they can replace their long-lived token. + if shouldSkipOAuth(profileData, out, g) { + return tokenSource, warningMessage, nil + } + + // If OAuth flow has never been executed for the defined token, then we're + // dealing with a user with a pre-existing traditional token and they've + // opted into the OAuth flow. + if noOAuthToken(profileData) { + warningMessage = "You've not authenticated via OAuth before" + tokenSource = forceReAuth() return tokenSource, warningMessage, nil } // Access Token has expired if auth.TokenExpired(profileData.AccessTokenTTL, profileData.AccessTokenCreated) { + // Refresh Token has expired if auth.TokenExpired(profileData.RefreshTokenTTL, profileData.RefreshTokenCreated) { - // TODO: Tweak warning message if dealing with long-lived token user. - // Specifically, we need to check if they opted to not skip OAuth. - // To do that might need changing the bool type of `SkipOAuth` to a - // pointer so we can check if it is nil rather than false (which is the - // zero value for that type). warningMessage = "Your access token has expired and so has your refresh token" - // To re-authenticate we simple reset the tokenSource variable. - // A later conditional block catches it and trigger a re-auth. - tokenSource = lookup.SourceUndefined - } else { - if !g.Flags.Quiet { - text.Info(out, "Your access token has now expired. We will attempt to refresh it") - } + tokenSource = forceReAuth() + return tokenSource, warningMessage, nil + } - account, _ := g.Account() - updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) - if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) - } - claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) - if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to verify refreshed JWT: %w", err) - } - email, ok := claims["email"] - if !ok { - return tokenSource, warningMessage, errors.New("failed to extract email from JWT claims") - } + if !g.Flags.Quiet { + text.Info(out, "Your access token has now expired. We will attempt to refresh it") + } - // Exchange the access token for a Fastly API token - apiEndpoint, _ := g.Endpoint() - at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) - if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to exchange access token for an API token: %w", err) - } + account, _ := g.Account() + updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) + if err != nil { + return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) + } + claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) + if err != nil { + return tokenSource, warningMessage, fmt.Errorf("failed to verify refreshed JWT: %w", err) + } + email, ok := claims["email"] + if !ok { + return tokenSource, warningMessage, errors.New("failed to extract email from JWT claims") + } - // NOTE: The refresh token can sometimes be refreshed along with the access token. - // This happens all the time in my testing but according to what is - // spec'd this apparently is something that _might_ happen. - // So after we get the refreshed access token, we check to see if the - // refresh token that was returned by the API call has also changed when - // compared to the refresh token stored in the CLI config file. - name, current := profile.Get(profileName, g.Config.Profiles) - if name == "" { - return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) - } - now := time.Now().Unix() - refreshToken := current.RefreshToken - refreshTokenCreated := current.RefreshTokenCreated - refreshTokenTTL := current.RefreshTokenTTL - if current.RefreshToken != updated.RefreshToken { - if !g.Flags.Quiet { - text.Info(out, "Your refresh token was also updated") - text.Break(out) - } - refreshToken = updated.RefreshToken - refreshTokenCreated = now - refreshTokenTTL = updated.RefreshExpiresIn - } + // Exchange the access token for a Fastly API token + apiEndpoint, _ := g.Endpoint() + at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) + if err != nil { + return tokenSource, warningMessage, fmt.Errorf("failed to exchange access token for an API token: %w", err) + } - ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { - p.AccessToken = updated.AccessToken - p.AccessTokenCreated = now - p.AccessTokenTTL = updated.ExpiresIn - p.Email = email.(string) - p.RefreshToken = refreshToken - p.RefreshTokenCreated = refreshTokenCreated - p.RefreshTokenTTL = refreshTokenTTL - p.Token = at.AccessToken - }) - if !ok { - return tokenSource, warningMessage, fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), - Remediation: "Run `fastly authenticate` to retry.", - } + // NOTE: The refresh token can sometimes be refreshed along with the access token. + // This happens all the time in my testing but according to what is + // spec'd this apparently is something that _might_ happen. + // So after we get the refreshed access token, we check to see if the + // refresh token that was returned by the API call has also changed when + // compared to the refresh token stored in the CLI config file. + name, current := profile.Get(profileName, g.Config.Profiles) + if name == "" { + return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) + } + now := time.Now().Unix() + refreshToken := current.RefreshToken + refreshTokenCreated := current.RefreshTokenCreated + refreshTokenTTL := current.RefreshTokenTTL + if current.RefreshToken != updated.RefreshToken { + if !g.Flags.Quiet { + text.Info(out, "Your refresh token was also updated") + text.Break(out) } - g.Config.Profiles = ps - if err := g.Config.Write(g.ConfigPath); err != nil { - g.ErrLog.Add(err) - return tokenSource, warningMessage, fmt.Errorf("error saving config file: %w", err) + refreshToken = updated.RefreshToken + refreshTokenCreated = now + refreshTokenTTL = updated.RefreshExpiresIn + } + + ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { + p.AccessToken = updated.AccessToken + p.AccessTokenCreated = now + p.AccessTokenTTL = updated.ExpiresIn + p.Email = email.(string) + p.RefreshToken = refreshToken + p.RefreshTokenCreated = refreshTokenCreated + p.RefreshTokenTTL = refreshTokenTTL + p.Token = at.AccessToken + }) + if !ok { + return tokenSource, warningMessage, fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), + Remediation: "Run `fastly authenticate` to retry.", } } + g.Config.Profiles = ps + if err := g.Config.Write(g.ConfigPath); err != nil { + g.ErrLog.Add(err) + return tokenSource, warningMessage, fmt.Errorf("error saving config file: %w", err) + } } } return tokenSource, warningMessage, nil } -// shouldSkipOAuth identifies if a config is a pre-v4 config and, if it is, will -// prompt the user to confirm if they want to swap their token for OAuth flow. -// -// If dealing with a long-lived token, we default to saying "yes" to keep it. -func shouldSkipOAuth(pd *config.Profile, in io.Reader, out io.Writer, g *global.Data) bool { - // If user has followed OAuth flow before, then these will not be zero values. - if pd.AccessToken == "" && pd.RefreshToken == "" && pd.AccessTokenCreated == 0 && pd.RefreshTokenCreated == 0 { - if g.Flags.AutoYes || g.Flags.NonInteractive || g.Env.AllowStaticToken == "1" { - return true +// shouldSkipOAuth identifies if a config is a pre-v4 config and, if it is, +// informs the user how they can use the OAuth flow. It checks if the OAuth +// environment variable has been set and enables the OAuth flow if so. +func shouldSkipOAuth(pd *config.Profile, out io.Writer, g *global.Data) bool { + if noOAuthToken(pd) { + if g.Env.UseOAuth == "1" { + return false // don't skip OAuth } - text.Info(out, "Your token doesn't appear to have been generated using Fastly's OAuth2 account flow (which offers more security as it uses short-lived tokens that can be automatically regenerated for a period of time).") - text.Break(out) - keepToken, err := text.AskYesNo(out, "Do you want to keep your current token? [y/N]: ", in) + text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_OAUTH=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") text.Break(out) - if err == nil && keepToken { - return true - } - return false + return true // skip OAuth } - return false + return false // don't skip OAuth +} + +func noOAuthToken(pd *config.Profile) bool { + // If user has followed OAuth flow before, then these will not be zero values. + return pd.AccessToken == "" && pd.RefreshToken == "" && pd.AccessTokenCreated == 0 && pd.RefreshTokenCreated == 0 +} + +// To re-authenticate we simply reset the tokenSource variable. +// A later conditional block catches it and trigger a re-auth. +func forceReAuth() lookup.Source { + return lookup.SourceUndefined } func authenticateUnlessTokenExists( @@ -406,14 +418,12 @@ func authenticateUnlessTokenExists( return token, tokenSource, nil } -func displayTokenIfVerbose(tokenSource lookup.Source, profileName string, g global.Data, out io.Writer) { - if g.Verbose() { - displayTokenSource( - tokenSource, - out, - determineProfile(profileName, g.Flags.Profile, g.Config.Profiles), - ) - } +func displayToken(tokenSource lookup.Source, profileName string, g global.Data, out io.Writer) { + displayTokenSource( + tokenSource, + out, + determineProfile(profileName, g.Flags.Profile, g.Config.Profiles), + ) } // If we are using the token from config file, check the file's permissions @@ -438,15 +448,15 @@ func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, verboseMo if verboseMode { switch endpointSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.Endpoint, endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) case lookup.SourceFile: fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) case lookup.SourceDefault, lookup.SourceUndefined: fallthrough default: - fmt.Fprintf(out, "Fastly API endpoint: %s\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) } } } @@ -454,12 +464,12 @@ func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, verboseMo func configureClients(token, endpoint string, acf APIClientFactory, debugMode bool) (apiClient api.Interface, rtsClient api.RealtimeStatsInterface, err error) { apiClient, err = acf(token, endpoint, debugMode) if err != nil { - return apiClient, rtsClient, fmt.Errorf("error constructing Fastly API client: %w", err) + return nil, nil, fmt.Errorf("error constructing Fastly API client: %w", err) } rtsClient, err = fastly.NewRealtimeStatsClientForEndpoint(token, fastly.DefaultRealtimeStatsEndpoint) if err != nil { - return apiClient, rtsClient, fmt.Errorf("error constructing Fastly realtime stats client: %w", err) + return nil, nil, fmt.Errorf("error constructing Fastly realtime stats client: %w", err) } return apiClient, rtsClient, nil @@ -492,15 +502,15 @@ type Versioners struct { func displayTokenSource(source lookup.Source, out io.Writer, profileSource string) { switch source { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API token provided via --token\n") + fmt.Fprintf(out, "Fastly API token provided via --token\n\n") case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API token provided via %s\n", env.APIToken) + fmt.Fprintf(out, "Fastly API token provided via %s\n\n", env.APIToken) case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API token provided via config file (profile: %s)\n", profileSource) - case lookup.SourceDefault, lookup.SourceUndefined: + fmt.Fprintf(out, "Fastly API token provided via config file (profile: %s)\n\n", profileSource) + case lookup.SourceUndefined, lookup.SourceDefault: fallthrough default: - fmt.Fprintf(out, "Fastly API token not provided\n") + fmt.Fprintf(out, "Fastly API token not provided\n\n") } } diff --git a/pkg/commands/acl/acl_test.go b/pkg/commands/acl/acl_test.go index 0ccdc23a9..39804d2d6 100644 --- a/pkg/commands/acl/acl_test.go +++ b/pkg/commands/acl/acl_test.go @@ -283,7 +283,7 @@ func TestACLList(t *testing.T) { ListACLsFn: listACLs, }, Args: args("acl list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: 456\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: 789\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\n", + WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: 456\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: 789\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\n", }, } diff --git a/pkg/commands/aclentry/aclentry_test.go b/pkg/commands/aclentry/aclentry_test.go index 839189957..6bc74c719 100644 --- a/pkg/commands/aclentry/aclentry_test.go +++ b/pkg/commands/aclentry/aclentry_test.go @@ -348,8 +348,8 @@ var listACLEntriesOutputPageTwo = `SERVICE ID ID IP SUBNET NEGATED 123 789 127.0.0.2 0 true ` -var listACLEntriesOutputVerbose = `Fastly API token provided via config file (profile: user) -Fastly API endpoint: https://api.fastly.com +var listACLEntriesOutputVerbose = `Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/authtoken/authtoken_test.go b/pkg/commands/authtoken/authtoken_test.go index f328c6a7a..be8195838 100644 --- a/pkg/commands/authtoken/authtoken_test.go +++ b/pkg/commands/authtoken/authtoken_test.go @@ -368,8 +368,8 @@ Expires at: 2021-06-15 23:00:00 +0000 UTC` } func listTokenOutputVerbose() string { - return `Fastly API token provided via config file (profile: user) -Fastly API endpoint: https://api.fastly.com + return `Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) ID: 123 diff --git a/pkg/commands/backend/backend_test.go b/pkg/commands/backend/backend_test.go index fd8cea69b..97b087867 100644 --- a/pkg/commands/backend/backend_test.go +++ b/pkg/commands/backend/backend_test.go @@ -477,8 +477,8 @@ SERVICE VERSION NAME ADDRESS PORT COMMENT `) + "\n" var listBackendsVerboseOutput = strings.Join([]string{ - "Fastly API token provided via config file (profile: user)", "Fastly API endpoint: https://api.fastly.com", + "Fastly API token provided via config file (profile: user)", "", "Service ID (via --service-id): 123", "", diff --git a/pkg/commands/dictionary/dictionary_test.go b/pkg/commands/dictionary/dictionary_test.go index c1f7a8b07..7aab4f36e 100644 --- a/pkg/commands/dictionary/dictionary_test.go +++ b/pkg/commands/dictionary/dictionary_test.go @@ -466,8 +466,8 @@ var ( var updateDictionaryOutputVerbose = strings.Join( []string{ - "Fastly API token provided via config file (profile: user)", "Fastly API endpoint: https://api.fastly.com", + "Fastly API token provided via config file (profile: user)", "", "Service ID (via --service-id): 123", "", @@ -511,8 +511,8 @@ Deleted (UTC): 2001-02-03 04:05 `) + "\n" var describeDictionaryOutputVerbose = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/domain/domain_test.go b/pkg/commands/domain/domain_test.go index 193c745ac..3f6bc19c3 100644 --- a/pkg/commands/domain/domain_test.go +++ b/pkg/commands/domain/domain_test.go @@ -369,8 +369,8 @@ SERVICE VERSION NAME COMMENT `) + "\n" var listDomainsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/healthcheck/healthcheck_test.go b/pkg/commands/healthcheck/healthcheck_test.go index 4dac9496b..e005d5268 100644 --- a/pkg/commands/healthcheck/healthcheck_test.go +++ b/pkg/commands/healthcheck/healthcheck_test.go @@ -317,8 +317,8 @@ SERVICE VERSION NAME METHOD HOST PATH `) + "\n" var listHealthChecksVerboseOutput = strings.Join([]string{ - "Fastly API token provided via config file (profile: user)", "Fastly API endpoint: https://api.fastly.com", + "Fastly API token provided via config file (profile: user)", "", "Service ID (via --service-id): 123", "", diff --git a/pkg/commands/logging/azureblob/azureblob_integration_test.go b/pkg/commands/logging/azureblob/azureblob_integration_test.go index 1b81368e3..e981767a6 100644 --- a/pkg/commands/logging/azureblob/azureblob_integration_test.go +++ b/pkg/commands/logging/azureblob/azureblob_integration_test.go @@ -343,8 +343,8 @@ SERVICE VERSION NAME `) + "\n" var listBlobStoragesVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/bigquery/bigquery_integration_test.go b/pkg/commands/logging/bigquery/bigquery_integration_test.go index 3458b014b..bd5ef3116 100644 --- a/pkg/commands/logging/bigquery/bigquery_integration_test.go +++ b/pkg/commands/logging/bigquery/bigquery_integration_test.go @@ -313,8 +313,8 @@ SERVICE VERSION NAME `) + "\n" var listBigQueriesVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go index 95beef2e9..ab5f12695 100644 --- a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go +++ b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go @@ -334,8 +334,8 @@ SERVICE VERSION NAME `) + "\n" var listCloudfilesVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/datadog/datadog_integration_test.go b/pkg/commands/logging/datadog/datadog_integration_test.go index c31ca799d..16c775831 100644 --- a/pkg/commands/logging/datadog/datadog_integration_test.go +++ b/pkg/commands/logging/datadog/datadog_integration_test.go @@ -310,8 +310,8 @@ SERVICE VERSION NAME `) + "\n" var listDatadogsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go index e35193d7e..2c09443c1 100644 --- a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go +++ b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go @@ -334,8 +334,8 @@ SERVICE VERSION NAME `) + "\n" var listDigitalOceansVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go index 46247e280..4c8981fff 100644 --- a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go +++ b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go @@ -338,8 +338,8 @@ SERVICE VERSION NAME `) + "\n" var listElasticsearchsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/ftp/ftp_integration_test.go b/pkg/commands/logging/ftp/ftp_integration_test.go index e53ea682b..1ad0e76e7 100644 --- a/pkg/commands/logging/ftp/ftp_integration_test.go +++ b/pkg/commands/logging/ftp/ftp_integration_test.go @@ -330,8 +330,8 @@ SERVICE VERSION NAME `) + "\n" var listFTPsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/gcs/gcs_integration_test.go b/pkg/commands/logging/gcs/gcs_integration_test.go index c876a5247..bd753b94e 100644 --- a/pkg/commands/logging/gcs/gcs_integration_test.go +++ b/pkg/commands/logging/gcs/gcs_integration_test.go @@ -329,8 +329,8 @@ SERVICE VERSION NAME `) + "\n" var listGCSsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go index 22be6e744..30aac45f8 100644 --- a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go +++ b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go @@ -320,8 +320,8 @@ SERVICE VERSION NAME `) + "\n" var listGooglePubSubsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/heroku/heroku_integration_test.go b/pkg/commands/logging/heroku/heroku_integration_test.go index 367771032..353060075 100644 --- a/pkg/commands/logging/heroku/heroku_integration_test.go +++ b/pkg/commands/logging/heroku/heroku_integration_test.go @@ -310,8 +310,8 @@ SERVICE VERSION NAME `) + "\n" var listHerokusVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go index 20cca51fe..ec3180d4d 100644 --- a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go +++ b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go @@ -310,8 +310,8 @@ SERVICE VERSION NAME `) + "\n" var listHoneycombsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/https/https_integration_test.go b/pkg/commands/logging/https/https_integration_test.go index efbaffe43..e66016c02 100644 --- a/pkg/commands/logging/https/https_integration_test.go +++ b/pkg/commands/logging/https/https_integration_test.go @@ -344,8 +344,8 @@ SERVICE VERSION NAME `) + "\n" var listHTTPSsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/kafka/kafka_integration_test.go b/pkg/commands/logging/kafka/kafka_integration_test.go index 60c1e833a..7b5ff907b 100644 --- a/pkg/commands/logging/kafka/kafka_integration_test.go +++ b/pkg/commands/logging/kafka/kafka_integration_test.go @@ -356,8 +356,8 @@ SERVICE VERSION NAME `) + "\n" var listKafkasVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/kinesis/kinesis_integration_test.go b/pkg/commands/logging/kinesis/kinesis_integration_test.go index e0b4e4523..960152d8d 100644 --- a/pkg/commands/logging/kinesis/kinesis_integration_test.go +++ b/pkg/commands/logging/kinesis/kinesis_integration_test.go @@ -351,8 +351,8 @@ SERVICE VERSION NAME `) + "\n" var listKinesesVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/loggly/loggly_integration_test.go b/pkg/commands/logging/loggly/loggly_integration_test.go index 444ebfe98..25bf6b2e2 100644 --- a/pkg/commands/logging/loggly/loggly_integration_test.go +++ b/pkg/commands/logging/loggly/loggly_integration_test.go @@ -308,8 +308,8 @@ SERVICE VERSION NAME `) + "\n" var listLogglysVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go index 305ac3388..120dbe1de 100644 --- a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go +++ b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go @@ -310,8 +310,8 @@ SERVICE VERSION NAME `) + "\n" var listLogshuttlesVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/newrelic/newrelic_test.go b/pkg/commands/logging/newrelic/newrelic_test.go index d82f3b605..43c910180 100644 --- a/pkg/commands/logging/newrelic/newrelic_test.go +++ b/pkg/commands/logging/newrelic/newrelic_test.go @@ -270,7 +270,7 @@ func TestNewRelicList(t *testing.T) { ListNewRelicFn: listNewRelic, }, Args: args("logging newrelic list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go index 78c220898..96cbc485b 100644 --- a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go +++ b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go @@ -270,7 +270,7 @@ func TestNewRelicList(t *testing.T) { ListNewRelicOTLPFn: listNewRelic, }, Args: args("logging newrelicotlp list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\n\nToken: \n\nFormat: \n\nFormat Version: 0\n\nPlacement: \n\nRegion: \n\nResponse Condition: \n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/logging/openstack/openstack_integration_test.go b/pkg/commands/logging/openstack/openstack_integration_test.go index cbb3078cb..97984613c 100644 --- a/pkg/commands/logging/openstack/openstack_integration_test.go +++ b/pkg/commands/logging/openstack/openstack_integration_test.go @@ -336,8 +336,8 @@ SERVICE VERSION NAME `) + "\n" var listOpenstacksVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/papertrail/papertrail_integration_test.go b/pkg/commands/logging/papertrail/papertrail_integration_test.go index cf8a74239..c41540c42 100644 --- a/pkg/commands/logging/papertrail/papertrail_integration_test.go +++ b/pkg/commands/logging/papertrail/papertrail_integration_test.go @@ -305,8 +305,8 @@ SERVICE VERSION NAME `) + "\n" var listPapertrailsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/s3/s3_integration_test.go b/pkg/commands/logging/s3/s3_integration_test.go index f12ad8627..3c5ce2e51 100644 --- a/pkg/commands/logging/s3/s3_integration_test.go +++ b/pkg/commands/logging/s3/s3_integration_test.go @@ -397,8 +397,8 @@ SERVICE VERSION NAME `) + "\n" var listS3sVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/scalyr/scalyr_integration_test.go b/pkg/commands/logging/scalyr/scalyr_integration_test.go index 4da79df7d..f6e9541a0 100644 --- a/pkg/commands/logging/scalyr/scalyr_integration_test.go +++ b/pkg/commands/logging/scalyr/scalyr_integration_test.go @@ -341,8 +341,8 @@ SERVICE VERSION NAME `) + "\n" var listScalyrsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/sftp/sftp_integration_test.go b/pkg/commands/logging/sftp/sftp_integration_test.go index f73f5253d..983c2f47a 100644 --- a/pkg/commands/logging/sftp/sftp_integration_test.go +++ b/pkg/commands/logging/sftp/sftp_integration_test.go @@ -339,8 +339,8 @@ SERVICE VERSION NAME `) + "\n" var listSFTPsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/splunk/splunk_integration_test.go b/pkg/commands/logging/splunk/splunk_integration_test.go index a366460f3..563738d96 100644 --- a/pkg/commands/logging/splunk/splunk_integration_test.go +++ b/pkg/commands/logging/splunk/splunk_integration_test.go @@ -313,8 +313,8 @@ SERVICE VERSION NAME `) + "\n" var listSplunksVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/sumologic/sumologic_integration_test.go b/pkg/commands/logging/sumologic/sumologic_integration_test.go index 8da1b50b9..e70420e5d 100644 --- a/pkg/commands/logging/sumologic/sumologic_integration_test.go +++ b/pkg/commands/logging/sumologic/sumologic_integration_test.go @@ -305,8 +305,8 @@ SERVICE VERSION NAME `) + "\n" var listSumologicsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/logging/syslog/syslog_integration_test.go b/pkg/commands/logging/syslog/syslog_integration_test.go index 4951d353b..6447d7f70 100644 --- a/pkg/commands/logging/syslog/syslog_integration_test.go +++ b/pkg/commands/logging/syslog/syslog_integration_test.go @@ -323,8 +323,8 @@ SERVICE VERSION NAME `) + "\n" var listSyslogsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/profile/profile_test.go b/pkg/commands/profile/profile_test.go index aa0d879fb..aebba8c59 100644 --- a/pkg/commands/profile/profile_test.go +++ b/pkg/commands/profile/profile_test.go @@ -817,6 +817,7 @@ func TestUpdate(t *testing.T) { }, }, Stdin: []string{ + "", // we skip SSO prompt "", // we skip updating the token "y", // we set the profile to be the default }, diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 75c4bf3b4..e79a218cb 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -83,10 +83,10 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { // Otherwise user has to create a token manually, then paste it when prompted. useOAuthFlow := true if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { - text.Info(out, "When updating a profile you can either paste in an already long-lived token or allow the Fastly CLI to regenerate a short-lived token that can be automatically refreshed.") + text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to regenerate a short-lived token that can be automatically refreshed.") text.Break(out) var err error - useOAuthFlow, err = text.AskYesNo(out, "Continue with regenerating a short-lived token? [y/N]: ", in) + useOAuthFlow, err = text.AskYesNo(out, "Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: ", in) text.Break(out) if err != nil { return err diff --git a/pkg/commands/service/service_test.go b/pkg/commands/service/service_test.go index dd27519db..4e028dec3 100644 --- a/pkg/commands/service/service_test.go +++ b/pkg/commands/service/service_test.go @@ -445,8 +445,8 @@ Bar 456 wasm 1 2015-03-14 12:59 `) + "\n" var listServicesVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service 1/3 ID: 123 @@ -592,8 +592,8 @@ Versions: 2 `) + "\n" var describeServiceVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 @@ -702,8 +702,8 @@ Versions: 2 `) + "\n" var searchServiceVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) ID: 123 Name: Foo diff --git a/pkg/commands/serviceauth/service_test.go b/pkg/commands/serviceauth/service_test.go index ede9c7099..6f5f8a215 100644 --- a/pkg/commands/serviceauth/service_test.go +++ b/pkg/commands/serviceauth/service_test.go @@ -99,7 +99,7 @@ func TestServiceAuthList(t *testing.T) { { args: args("service-auth list --verbose"), api: mock.API{ListServiceAuthorizationsFn: listServiceAuthOK}, - wantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nAuth ID: 123\nUser ID: 456\nService ID: 789\nPermission: read_only\n", + wantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nAuth ID: 123\nUser ID: 456\nService ID: 789\nPermission: read_only\n", }, } for testcaseIdx := range scenarios { diff --git a/pkg/commands/serviceversion/serviceversion_test.go b/pkg/commands/serviceversion/serviceversion_test.go index 74e6c7e3d..d2a8b7044 100644 --- a/pkg/commands/serviceversion/serviceversion_test.go +++ b/pkg/commands/serviceversion/serviceversion_test.go @@ -310,8 +310,8 @@ NUMBER ACTIVE LAST EDITED (UTC) `) + "\n" var listVersionsVerboseOutput = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 diff --git a/pkg/commands/tls/custom/certificate/certificate_test.go b/pkg/commands/tls/custom/certificate/certificate_test.go index 35ae0be11..3fc468b26 100644 --- a/pkg/commands/tls/custom/certificate/certificate_test.go +++ b/pkg/commands/tls/custom/certificate/certificate_test.go @@ -196,7 +196,7 @@ func TestList(t *testing.T) { }, }, Args: args("tls-custom certificate list --verbose"), - WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nID: " + mockResponseID + "\nIssued to: " + mockFieldValue + "\nIssuer: " + mockFieldValue + "\nName: " + mockFieldValue + "\nReplace: true\nSerial number: " + mockFieldValue + "\nSignature algorithm: " + mockFieldValue + "\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nID: " + mockResponseID + "\nIssued to: " + mockFieldValue + "\nIssuer: " + mockFieldValue + "\nName: " + mockFieldValue + "\nReplace: true\nSerial number: " + mockFieldValue + "\nSignature algorithm: " + mockFieldValue + "\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/user/user_test.go b/pkg/commands/user/user_test.go index e0a246bb4..da5d9fe48 100644 --- a/pkg/commands/user/user_test.go +++ b/pkg/commands/user/user_test.go @@ -386,8 +386,8 @@ bar@example.com bar superuser false current123 } func listVerboseOutput() string { - return fmt.Sprintf(`Fastly API token provided via config file (profile: user) -Fastly API endpoint: https://api.fastly.com + return fmt.Sprintf(`Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) %s%s`, describeUserOutput(), describeCurrentUserOutput()) } diff --git a/pkg/commands/vcl/custom/custom_test.go b/pkg/commands/vcl/custom/custom_test.go index f44aecffb..fb7d84bcf 100644 --- a/pkg/commands/vcl/custom/custom_test.go +++ b/pkg/commands/vcl/custom/custom_test.go @@ -354,7 +354,7 @@ func TestVCLCustomList(t *testing.T) { ListVCLsFn: listVCLs, }, Args: args("vcl custom list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nMain: false\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nMain: true\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nMain: false\nContent: \n# some vcl content\n\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/vcl/snippet/snippet_test.go b/pkg/commands/vcl/snippet/snippet_test.go index 3c29d8d1e..b19fc3787 100644 --- a/pkg/commands/vcl/snippet/snippet_test.go +++ b/pkg/commands/vcl/snippet/snippet_test.go @@ -409,7 +409,7 @@ func TestVCLSnippetList(t *testing.T) { ListSnippetsFn: listSnippets, }, Args: args("vcl snippet list --service-id 123 --verbose --version 1"), - WantOutput: "Fastly API token provided via config file (profile: user)\nFastly API endpoint: https://api.fastly.com\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: abc\nPriority: 0\nDynamic: true\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: abc\nPriority: 0\nDynamic: false\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", + WantOutput: "Fastly API endpoint: https://api.fastly.com\nFastly API token provided via config file (profile: user)\n\nService ID (via --service-id): 123\n\nService Version: 1\n\nName: foo\nID: abc\nPriority: 0\nDynamic: true\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n\nName: bar\nID: abc\nPriority: 0\nDynamic: false\nType: recv\nContent: \n# some vcl content\nCreated at: 2021-06-15 23:00:00 +0000 UTC\nUpdated at: 2021-06-15 23:00:00 +0000 UTC\nDeleted at: 2021-06-15 23:00:00 +0000 UTC\n", }, } diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index d85b73515..f4b92d57d 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -141,8 +141,8 @@ var basicResponse = whoami.VerifyResponse{ var basicOutput = "Alice Programmer \n" var basicOutputVerbose = strings.TrimSpace(` -Fastly API token provided via config file (profile: user) Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Customer ID: abc Customer name: Computer Company diff --git a/pkg/config/config.go b/pkg/config/config.go index 7cd4b2583..3b734e3a2 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -442,13 +442,13 @@ func (f *File) Write(path string) error { type Environment struct { // Account is the env var we look in for the Accounts endpoint. Account string - // AllowStaticToken enables a user to avoid a CLI interactive prompt regarding - // their token not being generated via the Fastly OAuth flow. - AllowStaticToken string // APIToken is the env var we look in for the Fastly API token. APIToken string // Endpoint is the env var we look in for the API endpoint. Endpoint string + // UseOAuth indicates if user wants to use OAuth token flow. + // 1: enabled, 0: disabled. + UseOAuth string // WasmMetadataDisable is the env var we look in to disable all data // collection related to a Wasm binary. // Set to "true" to disable all forms of data collection. @@ -459,8 +459,9 @@ type Environment struct { func (e *Environment) Read(state map[string]string) { e.APIToken = state[env.APIToken] e.Account = state[env.Account] - e.AllowStaticToken = state[env.AllowStaticToken] e.Endpoint = state[env.Endpoint] + e.Endpoint = state[env.Endpoint] + e.UseOAuth = state[env.UseOAuth] e.WasmMetadataDisable = state[env.WasmMetadataDisable] } diff --git a/pkg/env/env.go b/pkg/env/env.go index 85f8ff1a1..7e64af434 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -16,13 +16,11 @@ const ( // #nosec APIToken = "FASTLY_API_TOKEN" - // AllowStaticToken enables a user to avoid a CLI interactive prompt regarding - // their token not being generated via the Fastly OAuth flow. - // gosec flagged this: - // G101 (CWE-798): Potential hardcoded credentials - // Disabling as this isn't an API token but a boolean 1/0 (on/off). - // #nosec - AllowStaticToken = "FASTLY_ALLOW_STATIC_TOKEN" + // UseOAuth enables the CLI to validate the token as an OAuth token. + // These tokens aren't traditional tokens generated by the UI. + // Instead they generated via an OAuth flow (producing access/refresh tokens). + // Assigned value should be a boolean 1/0 (enable/disable). + UseOAuth = "FASTLY_USE_OAUTH" // Endpoint is the env var we look in for the API endpoint. Endpoint = "FASTLY_API_ENDPOINT" diff --git a/pkg/global/global.go b/pkg/global/global.go index 246d9d8da..a17c7a111 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -70,14 +70,17 @@ type Data struct { // - The `profile` manifest field's associated profile token. // - The 'default' profile associated token (if there is one). func (d *Data) Token() (string, lookup.Source) { + // --token if d.Flags.Token != "" { return d.Flags.Token, lookup.SourceFlag } + // FASTLY_API_TOKEN if d.Env.APIToken != "" { return d.Env.APIToken, lookup.SourceEnvironment } + // --profile if d.Flags.Profile != "" { for k, v := range d.Config.Profiles { if k == d.Flags.Profile { @@ -86,6 +89,7 @@ func (d *Data) Token() (string, lookup.Source) { } } + // `profile` field in fastly.toml if d.Manifest.File.Profile != "" { for k, v := range d.Config.Profiles { if k == d.Manifest.File.Profile { @@ -94,6 +98,7 @@ func (d *Data) Token() (string, lookup.Source) { } } + // [profile] section in app config for _, v := range d.Config.Profiles { if v.Default { return v.Token, lookup.SourceFile From 4314e98817bc8288d844086b775a98307070146a Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 15:48:47 +0100 Subject: [PATCH 033/115] refactor(app): group token processing logic --- pkg/app/run.go | 146 +++++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 72 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 677a9d229..5da72ad65 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -93,24 +93,15 @@ func Run(opts RunOpts) error { } endpoint, endpointSource := g.Endpoint() - if g.Verbose() { displayAPIEndpoint(endpoint, endpointSource, opts.Stdout) } - token, tokenSource, err := processToken(commands, commandName, opts.Manifest.File.Profile, &g, opts.Stdin, opts.Stdout) + token, err := processToken(commands, commandName, opts.Manifest, &g, opts.Stdin, opts.Stdout) if err != nil { return fmt.Errorf("failed to process token: %w", err) } - // Check for a profile override. - token, err = profile.Init(token, opts.Manifest, &g, opts.Stdin, opts.Stdout) - if err != nil { - return err - } - - checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, opts.Stdout) - g.APIClient, g.RTSClient, err = configureClients(token, endpoint, opts.APIClient, g.Flags.Debug) if err != nil { g.ErrLog.Add(err) @@ -181,33 +172,54 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { return app } -// processToken first checks if a profile token is defined in config and if so, -// it will validate if it has expired, and if it has it will attempt to refresh -// it. If both the access token and the refresh token has expired it will -// trigger the `fastly authenticate` command to execute. Either way the CLI -// config is updated to reflect the token that was either refreshed or -// regenerated from the authentication process. -func processToken(commands []cmd.Command, commandName, profileName string, g *global.Data, in io.Reader, out io.Writer) (token string, tokenSource lookup.Source, err error) { +// processToken handles all aspects related to the required API token. +// +// First we check if a profile token is defined in config and if so, we will +// validate if it has expired, and if it has we will attempt to refresh it. +// +// If both the access token and the refresh token has expired we will trigger +// the `fastly authenticate` command to execute. +// +// Either way, the CLI config is updated to reflect the token that was either +// refreshed or regenerated from the authentication process. +// +// Next, we check the config file's permissions. +// +// Finally, we check if there is a profile override in place (e.g. set via the +// --profile flag or using the `profile` field in the fastly.toml manifest). +func processToken(commands []cmd.Command, commandName string, m *manifest.Data, g *global.Data, in io.Reader, out io.Writer) (token string, err error) { warningMessage := "No API token could be found" + var tokenSource lookup.Source token, tokenSource = g.Token() // Check if token is from fastly.toml [profile] and refresh if expired. tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, out, g) if err != nil { - return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) + return token, fmt.Errorf("failed to check profile token: %w", err) } - // If no token, trigger authentication (unless tokenless command) - token, tokenSource, err = authenticateUnlessTokenExists(tokenSource, token, commandName, warningMessage, commands, in, out, g) + // If there's no token available, and we need one for the invoked command, + // then we'll trigger the SSO authentication flow. + if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { + token, tokenSource, err = ssoAuthentication(tokenSource, token, warningMessage, commands, in, out, g) + if err != nil { + return token, fmt.Errorf("failed to check profile token: %w", err) + } + } + + checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, out) + + // Check for a profile override. + token, err = profile.Init(token, m, g, in, out) if err != nil { - return token, tokenSource, fmt.Errorf("failed to check profile token: %w", err) + return token, err } if g.Verbose() { - displayToken(tokenSource, profileName, *g, out) + displayToken(tokenSource, m.File.Profile, *g, out) } - return token, tokenSource, nil + return token, nil } // checkProfileToken can potentially modify `tokenSource` to trigger a re-auth. @@ -375,55 +387,61 @@ func forceReAuth() lookup.Source { return lookup.SourceUndefined } -func authenticateUnlessTokenExists( +func ssoAuthentication( tokenSource lookup.Source, - token, commandName, warningMessage string, + token, warningMessage string, commands []cmd.Command, in io.Reader, out io.Writer, g *global.Data, ) (string, lookup.Source, error) { - if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { - for _, command := range commands { - if command.Name() == "authenticate" { - text.Warning(out, "%s. We need to open your browser to authenticate you.", warningMessage) - text.Break(out) - cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) - text.Break(out) - if err != nil { - return token, tokenSource, err - } - if !cont { - return token, tokenSource, nil - } - - g.SkipAuthPrompt = true // skip the same prompt in `authenticate` command flow - err = command.Exec(in, out) - if err != nil { - return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) - } - text.Break(out) + for _, command := range commands { + if command.Name() == "authenticate" { + text.Warning(out, "%s. We need to open your browser to authenticate you.", warningMessage) + text.Break(out) + cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) + text.Break(out) + if err != nil { + return token, tokenSource, err + } + if !cont { + return token, tokenSource, nil + } - break + g.SkipAuthPrompt = true // skip the same prompt in `authenticate` command flow + err = command.Exec(in, out) + if err != nil { + return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) } - } + text.Break(out) - // Recheck for token (should be persisted to profile data). - token, tokenSource = g.Token() - if tokenSource == lookup.SourceUndefined { - return token, tokenSource, fsterr.ErrNoToken + break } } + // Recheck for token (should be persisted to profile data). + token, tokenSource = g.Token() + if tokenSource == lookup.SourceUndefined { + return token, tokenSource, fsterr.ErrNoToken + } return token, tokenSource, nil } func displayToken(tokenSource lookup.Source, profileName string, g global.Data, out io.Writer) { - displayTokenSource( - tokenSource, - out, - determineProfile(profileName, g.Flags.Profile, g.Config.Profiles), - ) + profileSource := determineProfile(profileName, g.Flags.Profile, g.Config.Profiles) + + switch tokenSource { + case lookup.SourceFlag: + fmt.Fprintf(out, "Fastly API token provided via --token\n\n") + case lookup.SourceEnvironment: + fmt.Fprintf(out, "Fastly API token provided via %s\n\n", env.APIToken) + case lookup.SourceFile: + fmt.Fprintf(out, "Fastly API token provided via config file (profile: %s)\n\n", profileSource) + case lookup.SourceUndefined, lookup.SourceDefault: + fallthrough + default: + fmt.Fprintf(out, "Fastly API token not provided\n\n") + } } // If we are using the token from config file, check the file's permissions @@ -498,22 +516,6 @@ type Versioners struct { WasmTools github.AssetVersioner } -// displayTokenSource prints the token source. -func displayTokenSource(source lookup.Source, out io.Writer, profileSource string) { - switch source { - case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API token provided via --token\n\n") - case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API token provided via %s\n\n", env.APIToken) - case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API token provided via config file (profile: %s)\n\n", profileSource) - case lookup.SourceUndefined, lookup.SourceDefault: - fallthrough - default: - fmt.Fprintf(out, "Fastly API token not provided\n\n") - } -} - // determineProfile determines if the provided token was acquired via the // fastly.toml manifest, the --profile flag, or was a default profile from // within the config.toml application configuration. From d244549956904a1216339e19f001398d1527af86 Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 15:49:38 +0100 Subject: [PATCH 034/115] refactor(profile/create): update prompt description --- pkg/commands/profile/create.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 5af109b26..652aa8620 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -71,9 +71,9 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { // Otherwise user has to create a token manually, then paste it when prompted. useOAuthFlow := true if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { - text.Info(out, "You can create a profile in one of two ways. Either paste in an already long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed.") + text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed.") text.Break(out) - useOAuthFlow, err = text.AskYesNo(out, "Continue to generate a short-lived token? [y/N]: ", in) + useOAuthFlow, err = text.AskYesNo(out, "Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: ", in) text.Break(out) if err != nil { return err From c02a914e3a81373f3e9a10b94ea98975ff4e8596 Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 16:59:42 +0100 Subject: [PATCH 035/115] fix: profile logic --- pkg/commands/authenticate/root.go | 52 +++++++++++----- pkg/commands/profile/create.go | 31 ++++++---- pkg/commands/profile/update.go | 98 ++++++++++++++++++++++++------- 3 files changed, 133 insertions(+), 48 deletions(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 0c846ed0d..df0379ad0 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -22,6 +22,15 @@ import ( // It should be installed under the primary root command. type RootCommand struct { cmd.Base + + // IMPORTANT: The following fields are public to the `profile` subcommands. + + // NewProfile indicates if we should create a new profile. + NewProfile bool + // NewProfileName indicates the new profile name. + NewProfileName string + // ProfileDefault indicates if the affected profile should become the default. + ProfileDefault bool } // NewRootCommand returns a new command registered in the parent. @@ -34,10 +43,10 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { // Exec implements the command interface. func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { - if !c.Globals.SkipAuthPrompt { + if !c.Globals.SkipAuthPrompt && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { text.Warning(out, "We need to open your browser to authenticate you.") text.Break(out) - cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) + cont, err := text.AskYesNo(out, text.BoldYellow("Do you want to continue? [y/N]: "), in) text.Break(out) if err != nil { return err @@ -118,12 +127,28 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { profileDefault, _ := profile.Default(c.Globals.Config.Profiles) - // If no profiles configured at all, create a new default... - if profileOverride == "" && profileDefault == "" { - c.Globals.Config.Profiles = createNewDefaultProfile(c.Globals.Config.Profiles, ar) - } else { + switch { + case profileOverride == "" && profileDefault == "": // If no profiles configured at all, create a new default... + makeDefault := true + c.Globals.Config.Profiles = createNewProfile(profile.DefaultName, makeDefault, c.Globals.Config.Profiles, ar) + case c.NewProfile: // We know we've been triggered by `profile create` if this is set. + c.Globals.Config.Profiles = createNewProfile(c.NewProfileName, c.ProfileDefault, c.Globals.Config.Profiles, ar) + + // If the user wants the newly created profile to be their new default, then + // we'll call Set for its side effect of resetting all other profiles to have + // their Default field set to false. + if c.ProfileDefault { + if p, ok := profile.SetDefault(c.NewProfileName, c.Globals.Config.Profiles); ok { + c.Globals.Config.Profiles = p + } + } + default: // Otherwise, edit the existing profile to have the newly generated tokens. - ps, err := editProfile(profileDefault, profileOverride, c.Globals.Config.Profiles, ar) + profileName := profileDefault + if profileOverride != "" { + profileName = profileOverride + } + ps, err := editProfile(profileName, c.ProfileDefault, c.Globals.Config.Profiles, ar) if err != nil { return err } @@ -141,16 +166,16 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { // IMPORTANT: Mutates the config.Profiles map type. // We need to return the modified type so it can be safely reassigned. -func createNewDefaultProfile(p config.Profiles, ar auth.AuthorizationResult) config.Profiles { +func createNewProfile(profileName string, makeDefault bool, p config.Profiles, ar auth.AuthorizationResult) config.Profiles { now := time.Now().Unix() if p == nil { p = make(config.Profiles) } - p[profile.DefaultName] = &config.Profile{ + p[profileName] = &config.Profile{ AccessToken: ar.Jwt.AccessToken, AccessTokenCreated: now, AccessTokenTTL: ar.Jwt.ExpiresIn, - Default: true, + Default: makeDefault, Email: ar.Email, RefreshToken: ar.Jwt.RefreshToken, RefreshTokenCreated: now, @@ -162,13 +187,10 @@ func createNewDefaultProfile(p config.Profiles, ar auth.AuthorizationResult) con // IMPORTANT: Mutates the config.Profiles map type. // We need to return the modified type so it can be safely reassigned. -func editProfile(profileDefault, profileOverride string, p config.Profiles, ar auth.AuthorizationResult) (config.Profiles, error) { - profileName := profileDefault - if profileOverride != "" { - profileName = profileOverride - } +func editProfile(profileName string, profileDefault bool, p config.Profiles, ar auth.AuthorizationResult) (config.Profiles, error) { ps, ok := profile.Edit(profileName, p, func(p *config.Profile) { now := time.Now().Unix() + p.Default = profileDefault p.AccessToken = ar.Jwt.AccessToken p.AccessTokenCreated = now p.AccessTokenTTL = ar.Jwt.ExpiresIn diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 652aa8620..bab157cbb 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -56,9 +56,9 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { // an existing profile already set to be the default. In the latter scenario // we should prompt the user to see if the new profile they're creating needs // to become the new default. - def := true - if profileName, _ := profile.Default(c.Globals.Config.Profiles); profileName != "" { - def, err = c.promptForDefault(in, out) + makeDefault := true + if profileName, _ := profile.Default(c.Globals.Config.Profiles); profileName != "" && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { + makeDefault, err = c.promptForDefault(in, out) if err != nil { return err } @@ -71,22 +71,29 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { // Otherwise user has to create a token manually, then paste it when prompted. useOAuthFlow := true if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { - text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed.") - text.Break(out) - useOAuthFlow, err = text.AskYesNo(out, "Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: ", in) + text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed.\n\n") + useOAuthFlow, err = text.AskYesNo(out, text.BoldYellow("Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: "), in) text.Break(out) if err != nil { return err } } if useOAuthFlow { + // IMPORTANT: We need to set profile fields for authenticate command. + // + // This is so the `authenticate` command will use this information to create + // a new 'non-default' profile. + c.authCmd.NewProfile = true + c.authCmd.NewProfileName = c.profile + c.authCmd.ProfileDefault = makeDefault + err = c.authCmd.Exec(in, out) if err != nil { return fmt.Errorf("failed to authenticate: %w", err) } text.Break(out) } else { - if err := c.staticTokenFlow(def, in, out); err != nil { + if err := c.staticTokenFlow(makeDefault, in, out); err != nil { return err } } @@ -101,7 +108,7 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { } // staticTokenFlow initialises the token flow for a non-OAuth token. -func (c *CreateCommand) staticTokenFlow(def bool, in io.Reader, out io.Writer) error { +func (c *CreateCommand) staticTokenFlow(makeDefault bool, in io.Reader, out io.Writer) error { token, err := promptForToken(in, out, c.Globals.ErrLog) if err != nil { return err @@ -126,7 +133,7 @@ func (c *CreateCommand) staticTokenFlow(def bool, in io.Reader, out io.Writer) e return err } - return c.updateInMemCfg(email, token, endpoint, def, spinner) + return c.updateInMemCfg(email, token, endpoint, makeDefault, spinner) } func promptForToken(in io.Reader, out io.Writer, errLog fsterr.LogInterface) (string, error) { @@ -204,7 +211,7 @@ func (c *CreateCommand) validateToken(token, endpoint string, spinner text.Spinn } // updateInMemCfg persists the updated configuration data in-memory. -func (c *CreateCommand) updateInMemCfg(email, token, endpoint string, def bool, spinner text.Spinner) error { +func (c *CreateCommand) updateInMemCfg(email, token, endpoint string, makeDefault bool, spinner text.Spinner) error { return spinner.Process("Persisting configuration", func(_ *text.SpinnerWrapper) error { c.Globals.Config.Fastly.APIEndpoint = endpoint @@ -212,7 +219,7 @@ func (c *CreateCommand) updateInMemCfg(email, token, endpoint string, def bool, c.Globals.Config.Profiles = make(config.Profiles) } c.Globals.Config.Profiles[c.profile] = &config.Profile{ - Default: def, + Default: makeDefault, Email: email, Token: token, } @@ -220,7 +227,7 @@ func (c *CreateCommand) updateInMemCfg(email, token, endpoint string, def bool, // If the user wants the newly created profile to be their new default, then // we'll call SetDefault for its side effect of resetting all other profiles // to have their Default field set to false. - if def { + if makeDefault { if p, ok := profile.SetDefault(c.profile, c.Globals.Config.Profiles); ok { c.Globals.Config.Profiles = p } diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index e79a218cb..8c26c3206 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -46,20 +46,63 @@ func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { + profileName, p, err := c.identifyProfile() + if err != nil { + return fmt.Errorf("failed to identify the profile to update: %w", err) + } + text.Info(out, "Profile being updated: '%s'.\n\n", profileName) + + // Set to true for --auto-yes/--non-interactive flags, otherwise prompt user. + makeDefault := true + updateToken := true + + if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { + makeDefault, err = text.AskYesNo(out, text.BoldYellow("Make profile the default? [y/N] "), in) + text.Break(out) + if err != nil { + return err + } + + updateToken, err = text.AskYesNo(out, text.BoldYellow("Update the token associated with this profile? [y/N]: "), in) + if err != nil { + return err + } + } + + if updateToken { + err := c.updateToken(profileName, makeDefault, p, in, out) + if err != nil { + return fmt.Errorf("failed to update token: %w", err) + } + } else if makeDefault { + err := c.updateDefault(profileName, in, out) + if err != nil { + return fmt.Errorf("failed to update token: %w", err) + } + } + + text.Success(out, "\nProfile '%s' updated", profileName) + return nil +} + +func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { var ( profileName string p *config.Profile ) + // If profile argument not set and no --profile flag set, then identify the + // default profile to update. if c.profile == "" && c.Globals.Flags.Profile == "" { profileName, p = profile.Default(c.Globals.Config.Profiles) if profileName == "" { - return fsterr.RemediationError{ + return "", nil, fsterr.RemediationError{ Inner: fmt.Errorf("no active profile"), Remediation: profile.NoDefaults, } } } else { + // Otherwise, acquire the profile the user has specified. profileName = c.profile if c.Globals.Flags.Profile != "" { profileName = c.Globals.Flags.Profile @@ -67,15 +110,17 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { profileName, p = profile.Get(profileName, c.Globals.Config.Profiles) if profileName == "" { msg := fmt.Sprintf(profile.DoesNotExist, c.profile) - return fsterr.RemediationError{ + return "", nil, fsterr.RemediationError{ Inner: fmt.Errorf(msg), Remediation: fsterr.ProfileRemediation, } } } - text.Info(out, "Profile being updated: '%s'.\n\n", profileName) + return profileName, p, nil +} +func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *config.Profile, in io.Reader, out io.Writer) error { // Prompt user to decide on which token flow to take (OAuth or Static) // Static being a traditional long-lived user/automation token. // @@ -86,17 +131,21 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to regenerate a short-lived token that can be automatically refreshed.") text.Break(out) var err error - useOAuthFlow, err = text.AskYesNo(out, "Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: ", in) + useOAuthFlow, err = text.AskYesNo(out, text.BoldYellow("Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: "), in) text.Break(out) if err != nil { return err } } + if useOAuthFlow { // IMPORTANT: We need to temporarily set profile override. + // // This is so the `authenticate` command will use the override, rather than - // incorrectly updating the 'default' profile. + // incorrectly updating the 'default' profile. We also need to pass through + // whether the profile should be made the default. c.Globals.Flags.Profile = profileName + c.authCmd.ProfileDefault = makeDefault err := c.authCmd.Exec(in, out) if err != nil { @@ -104,17 +153,32 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { } text.Break(out) } else { - if err := c.staticTokenFlow(profileName, p, in, out); err != nil { + if err := c.staticTokenFlow(profileName, makeDefault, p, in, out); err != nil { return fmt.Errorf("failed to process the static token flow: %w", err) } } + // Write the in-memory representation back to disk. if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { c.Globals.ErrLog.Add(err) return fmt.Errorf("error saving config file: %w", err) } - text.Success(out, "\nProfile '%s' updated", profileName) + return nil +} + +func (c *UpdateCommand) updateDefault(profileName string, in io.Reader, out io.Writer) error { + p, ok := profile.SetDefault(profileName, c.Globals.Config.Profiles) + if !ok { + return errors.New("failed to update the profile's default field") + } + c.Globals.Config.Profiles = p + + // Write the in-memory representation back to disk. + if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { + c.Globals.ErrLog.Add(err) + return fmt.Errorf("error saving config file: %w", err) + } return nil } @@ -170,8 +234,7 @@ func (c *UpdateCommand) validateToken(token, endpoint string, spinner text.Spinn return user.Login, nil } -func (c *UpdateCommand) staticTokenFlow(profileName string, p *config.Profile, in io.Reader, out io.Writer) error { - var makeDefault bool +func (c *UpdateCommand) staticTokenFlow(profileName string, makeDefault bool, p *config.Profile, in io.Reader, out io.Writer) error { opts := []profile.EditOption{} token, err := text.InputSecure(out, text.BoldYellow("Profile token: (leave blank to skip): "), in) @@ -179,28 +242,21 @@ func (c *UpdateCommand) staticTokenFlow(profileName string, p *config.Profile, i c.Globals.ErrLog.Add(err) return err } - if token != "" { + + // User didn't want to change their token value so reassign original. + if token == "" { + token = p.Token + } else { opts = append(opts, func(p *config.Profile) { p.Token = token }) } - - text.Break(out) text.Break(out) - makeDefault, err = text.AskYesNo(out, "Make profile the default? [y/N] ", in) - if err != nil { - return err - } opts = append(opts, func(p *config.Profile) { p.Default = makeDefault }) - // User didn't want to change their token value so reassign original. - if token == "" { - token = p.Token - } - text.Break(out) spinner, err := text.NewSpinner(out) From 45bffff080b6309728161a1cd45b42916b2a1eb2 Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 17:55:01 +0100 Subject: [PATCH 036/115] fix: resolve semgrep concern for modified variable --- pkg/app/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 5da72ad65..14e779c88 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -212,7 +212,7 @@ func processToken(commands []cmd.Command, commandName string, m *manifest.Data, // Check for a profile override. token, err = profile.Init(token, m, g, in, out) if err != nil { - return token, err + return "", err } if g.Verbose() { From dcd8507ac01cd61633cafc0c371cef4d18203c5c Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 11 Sep 2023 18:11:47 +0100 Subject: [PATCH 037/115] refactor(authenticate): move profile logic to separate function --- pkg/commands/authenticate/root.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index df0379ad0..49fb9ef74 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -117,6 +117,17 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { } } + err = c.processProfiles(ar) + if err != nil { + c.Globals.ErrLog.Add(err) + return fmt.Errorf("failed to process profile data: %w", err) + } + + text.Success(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) + return nil +} + +func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { var profileOverride string switch { case c.Globals.Flags.Profile != "": @@ -156,11 +167,8 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { } if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { - c.Globals.ErrLog.Add(err) return fmt.Errorf("error saving config file: %w", err) } - - text.Success(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) return nil } From 06604fff7169e01b7a2d40b9518caf8dfb0825d6 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 10:28:47 +0100 Subject: [PATCH 038/115] fix(app): wrap prompt in flag checks --- pkg/app/run.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 14e779c88..c264003fe 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -397,24 +397,25 @@ func ssoAuthentication( ) (string, lookup.Source, error) { for _, command := range commands { if command.Name() == "authenticate" { - text.Warning(out, "%s. We need to open your browser to authenticate you.", warningMessage) - text.Break(out) - cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) - text.Break(out) - if err != nil { - return token, tokenSource, err - } - if !cont { - return token, tokenSource, nil + if !g.Flags.AutoYes && !g.Flags.NonInteractive { + text.Warning(out, "%s. We need to open your browser to authenticate you.", warningMessage) + text.Break(out) + cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) + text.Break(out) + if err != nil { + return token, tokenSource, err + } + if !cont { + return token, tokenSource, nil + } } g.SkipAuthPrompt = true // skip the same prompt in `authenticate` command flow - err = command.Exec(in, out) + err := command.Exec(in, out) if err != nil { return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) } text.Break(out) - break } } From 13739304a6dd151c1210e8f05c343330626a0942 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 10:29:00 +0100 Subject: [PATCH 039/115] doc(authenticate): explain prompt skip --- pkg/commands/authenticate/root.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 49fb9ef74..0bd0cf5f2 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -37,12 +37,17 @@ type RootCommand struct { func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand c.Globals = g - c.CmdClause = parent.Command("authenticate", "Authenticate with Fastly (returns temporary, auto-rotated, API token)") + c.CmdClause = parent.Command("authenticate", "SSO (Single Sign-On) authentication") return &c } // Exec implements the command interface. func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { + // We need to prompt the user, so they know we're about to open their web + // browser, but we also need to handle the scenario where the `authenticate` + // command is invoked indirectly via ../../app/run.go as that package will + // have its own (similar) prompt before invoking this command. So to avoid a + // double prompt, the app package will set `SkipAuthPrompt: true`. if !c.Globals.SkipAuthPrompt && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { text.Warning(out, "We need to open your browser to authenticate you.") text.Break(out) From 47621bf7394eba8b9d95b8bca537fcdecbc2f545 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 10:44:13 +0100 Subject: [PATCH 040/115] doc(authenticate): explain processProfiles logic --- pkg/commands/authenticate/root.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 0bd0cf5f2..f19ac856a 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -132,6 +132,12 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { return nil } +// processProfiles updates the relevant profile with the returned token data. +// +// First it checks the --profile flag and the `profile` fastly.toml field. +// Second it checks to see which profile is currently the default. +// Third it identifies which profile to be modified. +// Fourth it writes the updated in-memory data back to disk. func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { var profileOverride string switch { From 7abd86ff08bfc576f5cf9415100076a72612229b Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 11:05:08 +0100 Subject: [PATCH 041/115] refactor(authenticate): move case conditionals to separate functions --- pkg/commands/authenticate/root.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index f19ac856a..8fe8f5a12 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -150,10 +150,10 @@ func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { profileDefault, _ := profile.Default(c.Globals.Config.Profiles) switch { - case profileOverride == "" && profileDefault == "": // If no profiles configured at all, create a new default... + case noProfilesConfigured(profileOverride, profileDefault): makeDefault := true c.Globals.Config.Profiles = createNewProfile(profile.DefaultName, makeDefault, c.Globals.Config.Profiles, ar) - case c.NewProfile: // We know we've been triggered by `profile create` if this is set. + case invokedByProfileCreateCommand(c): c.Globals.Config.Profiles = createNewProfile(c.NewProfileName, c.ProfileDefault, c.Globals.Config.Profiles, ar) // If the user wants the newly created profile to be their new default, then @@ -183,6 +183,17 @@ func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { return nil } +// noProfilesConfigured determines if no profiles have been defined. +func noProfilesConfigured(o, d string) bool { + return o == "" && d == "" +} + +// invokedByProfileCreateCommand determines if this command was invoked by the +// `profile create` subcommand. +func invokedByProfileCreateCommand(c *RootCommand) bool { + return c.NewProfile && c.NewProfileName != "" +} + // IMPORTANT: Mutates the config.Profiles map type. // We need to return the modified type so it can be safely reassigned. func createNewProfile(profileName string, makeDefault bool, p config.Profiles, ar auth.AuthorizationResult) config.Profiles { From b9b2a6ed4556db5e85349d4eebdf4f44268de12f Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 11:07:54 +0100 Subject: [PATCH 042/115] doc(profile/update): clarify else if conditional --- pkg/commands/profile/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 8c26c3206..c7224e1c5 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -74,7 +74,7 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { if err != nil { return fmt.Errorf("failed to update token: %w", err) } - } else if makeDefault { + } else if makeDefault { // only set default if not updating the token (as updating the token already handles setting the default) err := c.updateDefault(profileName, in, out) if err != nil { return fmt.Errorf("failed to update token: %w", err) From c46517615dba4d452aa7359b68ad8c4771779127 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 11:31:08 +0100 Subject: [PATCH 043/115] fix: set Default correctly when invoking authenticate command directly --- pkg/commands/authenticate/root.go | 14 +++++++++++--- pkg/commands/profile/update.go | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 8fe8f5a12..8651f7624 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -31,6 +31,8 @@ type RootCommand struct { NewProfileName string // ProfileDefault indicates if the affected profile should become the default. ProfileDefault bool + // UpdateProfile indicates if we should update a profile. + UpdateProfile bool } // NewRootCommand returns a new command registered in the parent. @@ -170,7 +172,13 @@ func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { if profileOverride != "" { profileName = profileOverride } - ps, err := editProfile(profileName, c.ProfileDefault, c.Globals.Config.Profiles, ar) + makeDefault := c.ProfileDefault // this is set by `profile update` command. + if !c.UpdateProfile { // if not invoked by `profile update`, then get current `Default` field value + if n, p := profile.Get(profileName, c.Globals.Config.Profiles); n != "" { + makeDefault = p.Default + } + } + ps, err := editProfile(profileName, makeDefault, c.Globals.Config.Profiles, ar) if err != nil { return err } @@ -217,10 +225,10 @@ func createNewProfile(profileName string, makeDefault bool, p config.Profiles, a // IMPORTANT: Mutates the config.Profiles map type. // We need to return the modified type so it can be safely reassigned. -func editProfile(profileName string, profileDefault bool, p config.Profiles, ar auth.AuthorizationResult) (config.Profiles, error) { +func editProfile(profileName string, makeDefault bool, p config.Profiles, ar auth.AuthorizationResult) (config.Profiles, error) { ps, ok := profile.Edit(profileName, p, func(p *config.Profile) { now := time.Now().Unix() - p.Default = profileDefault + p.Default = makeDefault p.AccessToken = ar.Jwt.AccessToken p.AccessTokenCreated = now p.AccessTokenTTL = ar.Jwt.ExpiresIn diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index c7224e1c5..de425829b 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -143,8 +143,10 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con // // This is so the `authenticate` command will use the override, rather than // incorrectly updating the 'default' profile. We also need to pass through - // whether the profile should be made the default. + // an indicator that an existing profile should be updated and also whether + // that existing profile should now be made the default. c.Globals.Flags.Profile = profileName + c.authCmd.UpdateProfile = true c.authCmd.ProfileDefault = makeDefault err := c.authCmd.Exec(in, out) From 977d11015c486bc374d32fe5ee30af8ca1cc8787 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 11:44:12 +0100 Subject: [PATCH 044/115] refactor: clean-up profile.Get interface --- pkg/app/run.go | 4 ++-- pkg/commands/authenticate/root.go | 2 +- pkg/commands/profile/token.go | 2 +- pkg/commands/profile/update.go | 4 ++-- pkg/profile/profile.go | 16 ++++++++-------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index c264003fe..2251c5939 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -316,8 +316,8 @@ func checkProfileToken( // So after we get the refreshed access token, we check to see if the // refresh token that was returned by the API call has also changed when // compared to the refresh token stored in the CLI config file. - name, current := profile.Get(profileName, g.Config.Profiles) - if name == "" { + current := profile.Get(profileName, g.Config.Profiles) + if current == nil { return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) } now := time.Now().Unix() diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 8651f7624..e4f633c47 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -174,7 +174,7 @@ func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { } makeDefault := c.ProfileDefault // this is set by `profile update` command. if !c.UpdateProfile { // if not invoked by `profile update`, then get current `Default` field value - if n, p := profile.Get(profileName, c.Globals.Config.Profiles); n != "" { + if p := profile.Get(profileName, c.Globals.Config.Profiles); p != nil { makeDefault = p.Default } } diff --git a/pkg/commands/profile/token.go b/pkg/commands/profile/token.go index 3ab84f881..a08a47102 100644 --- a/pkg/commands/profile/token.go +++ b/pkg/commands/profile/token.go @@ -39,7 +39,7 @@ func (c *TokenCommand) Exec(_ io.Reader, out io.Writer) (err error) { } if p != "" { - if name, p := profile.Get(p, c.Globals.Config.Profiles); name != "" { + if p := profile.Get(p, c.Globals.Config.Profiles); p != nil { text.Output(out, p.Token) return nil } diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index de425829b..5825db294 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -107,8 +107,8 @@ func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { if c.Globals.Flags.Profile != "" { profileName = c.Globals.Flags.Profile } - profileName, p = profile.Get(profileName, c.Globals.Config.Profiles) - if profileName == "" { + p = profile.Get(profileName, c.Globals.Config.Profiles) + if p == nil { msg := fmt.Sprintf(profile.DoesNotExist, c.profile) return "", nil, fsterr.RemediationError{ Inner: fmt.Errorf(msg), diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index 0350d7d17..84b22b62b 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -43,13 +43,13 @@ func Default(p config.Profiles) (string, *config.Profile) { } // Get returns the specified profile. -func Get(name string, p config.Profiles) (string, *config.Profile) { +func Get(name string, p config.Profiles) *config.Profile { for k, v := range p { if k == name { - return k, v + return v } } - return "", new(config.Profile) + return nil } // SetDefault configures the named profile to be the default. @@ -127,14 +127,14 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W return token, nil } - name, p := Get(profile, g.Config.Profiles) - if name != "" { + p := Get(profile, g.Config.Profiles) + if p != nil { return p.Token, nil } msg := fmt.Sprintf(DoesNotExist, profile) - name, p = Default(g.Config.Profiles) - if name == "" { + profile, p = Default(g.Config.Profiles) + if profile == "" { msg = fmt.Sprintf("%s (no account profiles configured)", msg) return token, fsterr.RemediationError{ Inner: fmt.Errorf(msg), @@ -147,7 +147,7 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W // first letter so the warning reads like a proper sentence (where as golang // errors should always be lowercase). msg = fmt.Sprintf("%s%s. ", bytes.ToUpper([]byte(msg[:1])), msg[1:]) - msg = fmt.Sprintf("%sThe default profile '%s' (%s) will be used.", msg, name, p.Email) + msg = fmt.Sprintf("%sThe default profile '%s' (%s) will be used.", msg, profile, p.Email) if !g.Flags.AutoYes { text.Warning(out, msg) From 89f6e02bbbb8caa7888ee423dbd2cb9ce750359e Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 13:16:13 +0100 Subject: [PATCH 045/115] fix: refactor profile.Default interface --- pkg/commands/compute/init.go | 3 +-- pkg/commands/profile/create.go | 2 +- pkg/commands/profile/delete.go | 2 +- pkg/commands/profile/list.go | 2 +- pkg/commands/profile/token.go | 2 +- pkg/commands/profile/update.go | 2 +- pkg/profile/profile.go | 4 ++-- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pkg/commands/compute/init.go b/pkg/commands/compute/init.go index 67ac88588..18b755f16 100644 --- a/pkg/commands/compute/init.go +++ b/pkg/commands/compute/init.go @@ -139,8 +139,7 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) { // Assign the default profile email if available. email := "" - profileName, p := profile.Default(c.Globals.Config.Profiles) - if profileName != "" { + if _, p := profile.Default(c.Globals.Config.Profiles); p != nil { email = p.Email } diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index bab157cbb..d7915b4d4 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -57,7 +57,7 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { // we should prompt the user to see if the new profile they're creating needs // to become the new default. makeDefault := true - if profileName, _ := profile.Default(c.Globals.Config.Profiles); profileName != "" && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { + if _, p := profile.Default(c.Globals.Config.Profiles); p != nil && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { makeDefault, err = c.promptForDefault(in, out) if err != nil { return err diff --git a/pkg/commands/profile/delete.go b/pkg/commands/profile/delete.go index f490696ce..8641b9062 100644 --- a/pkg/commands/profile/delete.go +++ b/pkg/commands/profile/delete.go @@ -34,7 +34,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { } text.Success(out, "Profile '%s' deleted", c.profile) - if p, _ := profile.Default(c.Globals.Config.Profiles); p == "" && len(c.Globals.Config.Profiles) > 0 { + if _, p := profile.Default(c.Globals.Config.Profiles); p == nil && len(c.Globals.Config.Profiles) > 0 { text.Break(out) text.Warning(out, profile.NoDefaults) } diff --git a/pkg/commands/profile/list.go b/pkg/commands/profile/list.go index 1984c991b..f522251c4 100644 --- a/pkg/commands/profile/list.go +++ b/pkg/commands/profile/list.go @@ -52,7 +52,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { } name, p := profile.Default(c.Globals.Config.Profiles) - if name == "" { + if p == nil { text.Warning(out, profile.NoDefaults) } else { text.Info(out, "Default profile highlighted in red.\n\n") diff --git a/pkg/commands/profile/token.go b/pkg/commands/profile/token.go index a08a47102..d3cbab996 100644 --- a/pkg/commands/profile/token.go +++ b/pkg/commands/profile/token.go @@ -51,7 +51,7 @@ func (c *TokenCommand) Exec(_ io.Reader, out io.Writer) (err error) { } // If no 'profile' arg or global --profile, then we'll use 'active' profile. - if name, p := profile.Default(c.Globals.Config.Profiles); name != "" { + if _, p := profile.Default(c.Globals.Config.Profiles); p != nil { text.Output(out, p.Token) return nil } diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 5825db294..a28e32325 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -95,7 +95,7 @@ func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { // default profile to update. if c.profile == "" && c.Globals.Flags.Profile == "" { profileName, p = profile.Default(c.Globals.Config.Profiles) - if profileName == "" { + if p == nil { return "", nil, fsterr.RemediationError{ Inner: fmt.Errorf("no active profile"), Remediation: profile.NoDefaults, diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index 84b22b62b..ac135b639 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -39,7 +39,7 @@ func Default(p config.Profiles) (string, *config.Profile) { return k, v } } - return "", new(config.Profile) + return "", nil } // Get returns the specified profile. @@ -134,7 +134,7 @@ func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.W msg := fmt.Sprintf(DoesNotExist, profile) profile, p = Default(g.Config.Profiles) - if profile == "" { + if p == nil { msg = fmt.Sprintf("%s (no account profiles configured)", msg) return token, fsterr.RemediationError{ Inner: fmt.Errorf(msg), From a899b1429dc049a3e31a13023d59ad8e945853ba Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 13:34:04 +0100 Subject: [PATCH 046/115] refactor: rename OAuth to SSO --- pkg/app/run.go | 12 ++++++------ pkg/config/config.go | 6 +++--- pkg/env/env.go | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 2251c5939..49f4f68f1 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -262,8 +262,8 @@ func checkProfileToken( return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) } - // Allow user to opt-in to OAuth so they can replace their long-lived token. - if shouldSkipOAuth(profileData, out, g) { + // Allow user to opt-in to SSO/OAuth so they can replace their long-lived token. + if shouldSkipSSO(profileData, out, g) { return tokenSource, warningMessage, nil } @@ -361,15 +361,15 @@ func checkProfileToken( return tokenSource, warningMessage, nil } -// shouldSkipOAuth identifies if a config is a pre-v4 config and, if it is, +// shouldSkipSSO identifies if a config is a pre-v4 config and, if it is, // informs the user how they can use the OAuth flow. It checks if the OAuth // environment variable has been set and enables the OAuth flow if so. -func shouldSkipOAuth(pd *config.Profile, out io.Writer, g *global.Data) bool { +func shouldSkipSSO(pd *config.Profile, out io.Writer, g *global.Data) bool { if noOAuthToken(pd) { - if g.Env.UseOAuth == "1" { + if g.Env.UseSSO == "1" { return false // don't skip OAuth } - text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_OAUTH=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") + text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_SSO=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") text.Break(out) return true // skip OAuth } diff --git a/pkg/config/config.go b/pkg/config/config.go index 3b734e3a2..3a62b980c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -446,9 +446,9 @@ type Environment struct { APIToken string // Endpoint is the env var we look in for the API endpoint. Endpoint string - // UseOAuth indicates if user wants to use OAuth token flow. + // UseSSO indicates if user wants to use SSO/OAuth token flow. // 1: enabled, 0: disabled. - UseOAuth string + UseSSO string // WasmMetadataDisable is the env var we look in to disable all data // collection related to a Wasm binary. // Set to "true" to disable all forms of data collection. @@ -461,7 +461,7 @@ func (e *Environment) Read(state map[string]string) { e.Account = state[env.Account] e.Endpoint = state[env.Endpoint] e.Endpoint = state[env.Endpoint] - e.UseOAuth = state[env.UseOAuth] + e.UseSSO = state[env.UseSSO] e.WasmMetadataDisable = state[env.WasmMetadataDisable] } diff --git a/pkg/env/env.go b/pkg/env/env.go index 7e64af434..5eb3060e0 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -16,11 +16,11 @@ const ( // #nosec APIToken = "FASTLY_API_TOKEN" - // UseOAuth enables the CLI to validate the token as an OAuth token. + // UseSSO enables the CLI to validate the token as an OAuth token. // These tokens aren't traditional tokens generated by the UI. // Instead they generated via an OAuth flow (producing access/refresh tokens). // Assigned value should be a boolean 1/0 (enable/disable). - UseOAuth = "FASTLY_USE_OAUTH" + UseSSO = "FASTLY_USE_SSO" // Endpoint is the env var we look in for the API endpoint. Endpoint = "FASTLY_API_ENDPOINT" From fcdce4ee393c8e4be56aafa060c36af7377a47d8 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 14:07:21 +0100 Subject: [PATCH 047/115] refactor: inject browser opener behaviour --- cmd/fastly/main.go | 2 ++ pkg/app/commands.go | 2 +- pkg/app/run.go | 38 ++++++++++++++++++++++--------- pkg/commands/authenticate/root.go | 7 +++--- pkg/config/config.go | 2 ++ pkg/testutil/args.go | 7 +++++- 6 files changed, 42 insertions(+), 16 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index 6b8c62cb6..181801e2d 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fatih/color" + "github.com/skratchdot/open-golang/open" "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/app" @@ -100,6 +101,7 @@ func main() { ExecuteWasmTools: compute.ExecuteWasmTools, HTTPClient: httpClient, Manifest: &md, + Opener: open.Run, Stdin: in, Stdout: out, Versioners: app.Versioners{ diff --git a/pkg/app/commands.go b/pkg/app/commands.go index 1d82bf4cc..f71760cd1 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -100,7 +100,7 @@ func defineCommands( aclEntryDescribe := aclentry.NewDescribeCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryList := aclentry.NewListCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryUpdate := aclentry.NewUpdateCommand(aclEntryCmdRoot.CmdClause, g, m) - authenticateCmdRoot := authenticate.NewRootCommand(app, g) + authenticateCmdRoot := authenticate.NewRootCommand(app, g, opts.Opener) authtokenCmdRoot := authtoken.NewRootCommand(app, g) authtokenCreate := authtoken.NewCreateCommand(authtokenCmdRoot.CmdClause, g, m) authtokenDelete := authtoken.NewDeleteCommand(authtokenCmdRoot.CmdClause, g, m) diff --git a/pkg/app/run.go b/pkg/app/run.go index 49f4f68f1..ba22d6c3e 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -116,18 +116,34 @@ func Run(opts RunOpts) error { // RunOpts represent arguments to Run(). type RunOpts struct { - APIClient APIClientFactory - Args []string - ConfigFile config.File - ConfigPath string - Env config.Environment - ErrLog fsterr.LogInterface + // APIClient is a factory function for creating an api.Interface type. + APIClient APIClientFactory + // Args are the command line arguments provided by the user. + Args []string + // ConfigFile is an instance of the CLI application config. + ConfigFile config.File + // ConfigPath is the location of the CLI application config. + ConfigPath string + // Env is an instance of the supported environment variables. + Env config.Environment + // ErrLog is an instance of a error log recorder. + ErrLog fsterr.LogInterface + // ExecuteWasmTools is a function that calls wasm-tools executable. + // Designed to be used for mocking in the CLI test suite. ExecuteWasmTools func(bin string, args []string) error - HTTPClient api.HTTPClient - Manifest *manifest.Data - Stdin io.Reader - Stdout io.Writer - Versioners Versioners + // HTTPClient is a standard HTTP client. + HTTPClient api.HTTPClient + // Manifest is the fastly.toml manifest file. + Manifest *manifest.Data + // Opener is a function that can open a browser window. + Opener func(string) error + // Stdin is the standard input destination. + Stdin io.Reader + // Stdout is the standard output destination. + Stdout io.Writer + // Versioners contains multiple software versioning checkers. + // e.g. Check for latest CLI or Viceroy version. + Versioners Versioners } func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index e4f633c47..9b3cb8f39 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -7,7 +7,6 @@ import ( "time" "github.com/hashicorp/cap/oidc" - "github.com/skratchdot/open-golang/open" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" @@ -22,6 +21,7 @@ import ( // It should be installed under the primary root command. type RootCommand struct { cmd.Base + openBrowser func(string) error // IMPORTANT: The following fields are public to the `profile` subcommands. @@ -36,8 +36,9 @@ type RootCommand struct { } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) error) *RootCommand { var c RootCommand + c.openBrowser = opener c.Globals = g c.CmdClause = parent.Command("authenticate", "SSO (Single Sign-On) authentication") return &c @@ -111,7 +112,7 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { text.Break(out) text.Description(out, "We're opening the following URL in your default web browser so you may authenticate with Fastly", authorizationURL) - err = open.Run(authorizationURL) + err = c.openBrowser(authorizationURL) if err != nil { return fmt.Errorf("failed to open your default browser: %w", err) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 3a62b980c..31deee319 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -227,11 +227,13 @@ type File struct { } // SetAutoYes sets the associated flag value. +// This controls how the interactive prompts are handled. func (f *File) SetAutoYes(v bool) { f.autoYes = v } // SetNonInteractive sets the associated flag value. +// This controls how the interactive prompts are handled. func (f *File) SetNonInteractive(v bool) { f.nonInteractive = v } diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index a5075e53a..04cb3c4eb 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -1,6 +1,7 @@ package testutil import ( + "fmt" "io" "net/http" "regexp" @@ -82,7 +83,11 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { }, HTTPClient: &http.Client{Timeout: time.Second * 5}, Manifest: &md, - Stdout: stdout, + Opener: func(input string) error { + fmt.Printf("open url: %s\n", input) + return nil // no-op + }, + Stdout: stdout, } } From 72d7dee0fe7bac417dee44213a40fe4c2562ee92 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 15:40:07 +0100 Subject: [PATCH 048/115] refactor: inject auth server --- cmd/fastly/main.go | 13 +++++++ pkg/app/commands.go | 2 +- pkg/app/run.go | 3 ++ pkg/auth/auth.go | 56 +++++++++++++++++++++++++------ pkg/commands/authenticate/root.go | 34 ++++++------------- pkg/testutil/args.go | 6 ++-- 6 files changed, 77 insertions(+), 37 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index 181801e2d..bf8a36866 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -14,6 +14,7 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/commands/compute" "github.com/fastly/cli/pkg/config" "github.com/fastly/cli/pkg/env" @@ -84,6 +85,17 @@ func main() { // NOTE: We skip handling the error because not all commands relate to Compute. _ = md.File.Read(manifest.Filename) + // Configure authentication inputs. + // We do this here so that we can mock the values in our test suite. + result := make(chan auth.AuthorizationResult) + router := http.NewServeMux() + s := &auth.Server{ + HTTPClient: httpClient, + Result: result, + Router: router, + } + router.HandleFunc("/callback", s.HandleCallback()) + // The `main` function is a shim for calling `app.Run()`. err = app.Run(app.RunOpts{ APIClient: func(token, endpoint string, debugMode bool) (api.Interface, error) { @@ -94,6 +106,7 @@ func main() { return client, err }, Args: args, + AuthServer: s, ConfigFile: cfg, ConfigPath: config.FilePath, Env: e, diff --git a/pkg/app/commands.go b/pkg/app/commands.go index f71760cd1..69b2b0e33 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -100,7 +100,7 @@ func defineCommands( aclEntryDescribe := aclentry.NewDescribeCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryList := aclentry.NewListCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryUpdate := aclentry.NewUpdateCommand(aclEntryCmdRoot.CmdClause, g, m) - authenticateCmdRoot := authenticate.NewRootCommand(app, g, opts.Opener) + authenticateCmdRoot := authenticate.NewRootCommand(app, g, opts.Opener, opts.AuthServer) authtokenCmdRoot := authtoken.NewRootCommand(app, g) authtokenCreate := authtoken.NewCreateCommand(authtokenCmdRoot.CmdClause, g, m) authtokenDelete := authtoken.NewDeleteCommand(authtokenCmdRoot.CmdClause, g, m) diff --git a/pkg/app/run.go b/pkg/app/run.go index ba22d6c3e..34b971f1f 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -120,6 +120,9 @@ type RunOpts struct { APIClient APIClientFactory // Args are the command line arguments provided by the user. Args []string + // AuthServer is an instance of the authentication server type. + // Used for interacting with Fastly's SSO/OAuth authentication provider. + AuthServer auth.Starter // ConfigFile is an instance of the CLI application config. ConfigFile config.File // ConfigPath is the location of the CLI application config. diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 3994347ec..ff54bde26 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -27,21 +27,54 @@ const ClientID = "fastly-cli" // RedirectURL is the endpoint the auth provider will pass an authorization code to. const RedirectURL = "http://localhost:8080/callback" +// Starter defines the behaviour for the authentication server. +type Starter interface { + // GetResult returns the results channel + GetResult() chan AuthorizationResult + // SetAccountEndpoint sets the account endpoint. + SetAccountEndpoint(endpoint string) + // SetEndpoint sets the API endpoint. + SetAPIEndpoint(endpoint string) + // SetVerifier sets the code verifier. + SetVerifier(verifier *oidc.S256Verifier) + // Start starts a local server for handling authentication processing. + Start() error +} + // Server is a local server responsible for authentication processing. type Server struct { + // APIEndpoint is the API endpoint. + APIEndpoint string // AccountEndpoint is the accounts endpoint. AccountEndpoint string - // HTTPClient is a HTTP client used to call the API to exchange the access - // token for a session token. + // HTTPClient is a HTTP client used to call the API to exchange the access token for a session token. HTTPClient api.HTTPClient // Result is a channel that reports the result of authorization. Result chan AuthorizationResult // Router is an HTTP request multiplexer. Router *http.ServeMux - // Verifier represents an OAuth PKCE code verifier that uses the S256 challenge method + // Verifier represents an OAuth PKCE code verifier that uses the S256 challenge method. Verifier *oidc.S256Verifier - // APIEndpoint is the API endpoint. - APIEndpoint string +} + +// GetResult returns the result channel. +func (s Server) GetResult() chan AuthorizationResult { + return s.Result +} + +// SetAccountEndpoint sets the account endpoint. +func (s *Server) SetAccountEndpoint(endpoint string) { + s.AccountEndpoint = endpoint +} + +// SetAPIEndpoint sets the API endpoint. +func (s *Server) SetAPIEndpoint(endpoint string) { + s.APIEndpoint = endpoint +} + +// SetVerifier sets the code verifier endpoint. +func (s *Server) SetVerifier(verifier *oidc.S256Verifier) { + s.Verifier = verifier } // Start starts a local server for handling authentication processing. @@ -63,12 +96,8 @@ func (s *Server) Start() error { return nil } -// Routes configures the callback handler. -func (s *Server) Routes() { - s.Router.HandleFunc("/callback", s.handleCallback()) -} - -func (s *Server) handleCallback() http.HandlerFunc { +// HandleCallback processes the callback from the authentication service. +func (s *Server) HandleCallback() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { authorizationCode := r.URL.Query().Get("code") if authorizationCode == "" { @@ -153,6 +182,11 @@ type AuthorizationResult struct { SessionToken string } +// GenVerifier creates a code verifier. +func GenVerifier() (*oidc.S256Verifier, error) { + return oidc.NewCodeVerifier() +} + // GenURL constructs the required authorization_endpoint path. func GenURL(accountEndpoint, apiEndpoint string, verifier *oidc.S256Verifier) (string, error) { challenge, err := oidc.CreateCodeChallenge(verifier) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 9b3cb8f39..69911f23b 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -3,11 +3,8 @@ package authenticate import ( "fmt" "io" - "net/http" "time" - "github.com/hashicorp/cap/oidc" - "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/config" @@ -21,6 +18,7 @@ import ( // It should be installed under the primary root command. type RootCommand struct { cmd.Base + authServer auth.Starter openBrowser func(string) error // IMPORTANT: The following fields are public to the `profile` subcommands. @@ -36,8 +34,9 @@ type RootCommand struct { } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) error) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) error, authServer auth.Starter) *RootCommand { var c RootCommand + c.authServer = authServer c.openBrowser = opener c.Globals = g c.CmdClause = parent.Command("authenticate", "SSO (Single Sign-On) authentication") @@ -64,37 +63,26 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { } } - verifier, err := oidc.NewCodeVerifier() + accountEndpoint, _ := c.Globals.Account() + apiEndpoint, _ := c.Globals.Endpoint() + verifier, err := auth.GenVerifier() if err != nil { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to generate a code verifier: %w", err), Remediation: auth.Remediation, } } - - result := make(chan auth.AuthorizationResult) - apiEndpoint, _ := c.Globals.Endpoint() - accountEndpoint, _ := c.Globals.Account() - - s := auth.Server{ - APIEndpoint: apiEndpoint, - AccountEndpoint: accountEndpoint, - HTTPClient: c.Globals.HTTPClient, - Result: result, - Router: http.NewServeMux(), - Verifier: verifier, - } - s.Routes() + c.authServer.SetAccountEndpoint(accountEndpoint) + c.authServer.SetAPIEndpoint(apiEndpoint) + c.authServer.SetVerifier(verifier) var serverErr error - go func() { - err := s.Start() + err := c.authServer.Start() if err != nil { serverErr = err } }() - if serverErr != nil { return serverErr } @@ -117,7 +105,7 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { return fmt.Errorf("failed to open your default browser: %w", err) } - ar := <-result + ar := <-c.authServer.GetResult() if ar.Err != nil || ar.SessionToken == "" { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to authorize: %w", ar.Err), diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index 04cb3c4eb..214cc9745 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -9,6 +9,7 @@ import ( "time" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/config" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/manifest" @@ -70,8 +71,9 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { } return app.RunOpts{ - APIClient: mock.APIClient(mock.API{}), - Args: args, + Args: args, + APIClient: mock.APIClient(mock.API{}), + AuthServer: &auth.Server{}, ConfigFile: config.File{ Profiles: TokenProfile(), }, From c20b495d7cf6d5b81a4973b9d227595800e46c8d Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 17:03:35 +0100 Subject: [PATCH 049/115] test(authenticate): add validation for authenticate command --- pkg/commands/authenticate/auth_test.go | 179 +++++++++++++++++++++++++ pkg/commands/authenticate/root.go | 7 +- pkg/testutil/args.go | 33 ++++- 3 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 pkg/commands/authenticate/auth_test.go diff --git a/pkg/commands/authenticate/auth_test.go b/pkg/commands/authenticate/auth_test.go new file mode 100644 index 000000000..4d4420bd0 --- /dev/null +++ b/pkg/commands/authenticate/auth_test.go @@ -0,0 +1,179 @@ +package authenticate_test + +import ( + "bytes" + "errors" + "fmt" + "io" + "strings" + "testing" + "time" + + "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/auth" + "github.com/fastly/cli/pkg/config" + "github.com/fastly/cli/pkg/mock" + "github.com/fastly/cli/pkg/testutil" +) + +func TestAuth(t *testing.T) { + args := testutil.Args + type ts struct { + testutil.TestScenario + + AuthResult *auth.AuthorizationResult + ConfigProfile *config.Profile + Opener func(input string) error + Stdin []string + } + scenarios := []ts{ + // User cancels authentication prompt + { + TestScenario: testutil.TestScenario{ + Args: args("authenticate"), + WantError: "user cancelled execution", + }, + Stdin: []string{ + "N", // when prompted to open a web browser to start authentication + }, + }, + // Error opening web browser + { + TestScenario: testutil.TestScenario{ + Args: args("authenticate"), + WantError: "failed to open web browser", + }, + Opener: func(input string) error { + return errors.New("failed to open web browser") + }, + Stdin: []string{ + "Y", // when prompted to open a web browser to start authentication + }, + }, + // Error processing OAuth flow (error encountered) + { + TestScenario: testutil.TestScenario{ + Args: args("authenticate"), + WantError: "failed to authorize: no authorization code returned", + }, + AuthResult: &auth.AuthorizationResult{ + Err: errors.New("no authorization code returned"), + }, + Stdin: []string{ + "Y", // when prompted to open a web browser to start authentication + }, + }, + // Error processing OAuth flow (empty SessionToken field) + { + TestScenario: testutil.TestScenario{ + Args: args("authenticate"), + WantError: "failed to authorize: no session token", + }, + AuthResult: &auth.AuthorizationResult{ + SessionToken: "", + }, + Stdin: []string{ + "Y", // when prompted to open a web browser to start authentication + }, + }, + // Success processing OAuth flow + { + TestScenario: testutil.TestScenario{ + Args: args("authenticate"), + WantOutput: "Session token (persisted to your local configuration): 123", + }, + AuthResult: &auth.AuthorizationResult{ + SessionToken: "123", + }, + ConfigProfile: &config.Profile{ + Token: "123", + }, + Stdin: []string{ + "Y", // when prompted to open a web browser to start authentication + }, + }, + } + + for testcaseIdx := range scenarios { + testcase := &scenarios[testcaseIdx] + t.Run(testcase.Name, func(t *testing.T) { + var stdout bytes.Buffer + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClient = mock.APIClient(testcase.API) + + if testcase.AuthResult != nil { + result := make(chan auth.AuthorizationResult) + opts.AuthServer = testutil.MockAuthServer{ + Result: result, + } + go func() { + result <- *testcase.AuthResult + }() + } + if testcase.Opener != nil { + opts.Opener = testcase.Opener + } + + var err error + + if len(testcase.Stdin) > 1 { + // To handle multiple prompt input from the user we need to do some + // coordination around io pipes to mimic the required user behaviour. + stdin, prompt := io.Pipe() + opts.Stdin = stdin + + // Wait for user input and write it to the prompt + inputc := make(chan string) + go func() { + for input := range inputc { + fmt.Fprintln(prompt, input) + } + }() + + // We need a channel so we wait for `Run()` to complete + done := make(chan bool) + + // Call `app.Run()` and wait for response + go func() { + err = app.Run(opts) + done <- true + }() + + // User provides input + // + // NOTE: Must provide as much input as is expected to be waited on by `run()`. + // For example, if `run()` calls `input()` twice, then provide two messages. + // Otherwise the select statement will trigger the timeout error. + for _, input := range testcase.Stdin { + inputc <- input + } + + select { + case <-done: + // Wait for app.Run() to finish + case <-time.After(10 * time.Second): + t.Fatalf("unexpected timeout waiting for mocked prompt inputs to be processed") + } + } else { + stdin := "" + if len(testcase.Stdin) > 0 { + stdin = testcase.Stdin[0] + } + opts.Stdin = strings.NewReader(stdin) + err = app.Run(opts) + } + + if testcase.ConfigProfile != nil { + userProfile := opts.ConfigFile.Profiles["user"] + if userProfile.Token != testcase.ConfigProfile.Token { + t.Errorf("want token: %s, got token: %s", testcase.ConfigProfile.Token, userProfile.Token) + } + } + + t.Log(stdout.String()) + + testutil.AssertErrorContains(t, err, testcase.WantError) + testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) + }) + } +} diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 69911f23b..9254d92e2 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -1,6 +1,7 @@ package authenticate import ( + "errors" "fmt" "io" "time" @@ -107,8 +108,12 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { ar := <-c.authServer.GetResult() if ar.Err != nil || ar.SessionToken == "" { + err := ar.Err + if ar.Err == nil { + err = errors.New("no session token") + } return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to authorize: %w", ar.Err), + Inner: fmt.Errorf("failed to authorize: %w", err), Remediation: auth.Remediation, } } diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index 214cc9745..ac84fcbe0 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -1,13 +1,14 @@ package testutil import ( - "fmt" "io" "net/http" "regexp" "strings" "time" + "github.com/hashicorp/cap/oidc" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/config" @@ -55,6 +56,33 @@ func Args(args string) []string { return s } +// MockAuthServer is used to no-op the authentication server. +type MockAuthServer struct { + auth.Starter + + Result chan auth.AuthorizationResult +} + +func (s MockAuthServer) GetResult() chan auth.AuthorizationResult { + return s.Result +} + +func (s MockAuthServer) SetAccountEndpoint(_ string) { + // no-op +} + +func (s MockAuthServer) SetAPIEndpoint(_ string) { + // no-op +} + +func (s MockAuthServer) SetVerifier(_ *oidc.S256Verifier) { + // no-op +} + +func (s MockAuthServer) Start() error { + return nil // no-op +} + // NewRunOpts returns a struct that can be used to populate a call to app.Run() // while the majority of fields will be pre-populated and only those fields // commonly changed for testing purposes will need to be provided. @@ -73,7 +101,7 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { return app.RunOpts{ Args: args, APIClient: mock.APIClient(mock.API{}), - AuthServer: &auth.Server{}, + AuthServer: &MockAuthServer{}, ConfigFile: config.File{ Profiles: TokenProfile(), }, @@ -86,7 +114,6 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { HTTPClient: &http.Client{Timeout: time.Second * 5}, Manifest: &md, Opener: func(input string) error { - fmt.Printf("open url: %s\n", input) return nil // no-op }, Stdout: stdout, From b8329641b8970a7ae6b7aa337ee8fc5b5fa0ab59 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 12 Sep 2023 18:11:13 +0100 Subject: [PATCH 050/115] doc(app): clarify env var usage --- pkg/app/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 34b971f1f..e13e3f107 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -388,7 +388,7 @@ func shouldSkipSSO(pd *config.Profile, out io.Writer, g *global.Data) bool { if g.Env.UseSSO == "1" { return false // don't skip OAuth } - text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_SSO=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") + text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_SSO=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO for the profile you used when authenticating with SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") text.Break(out) return true // skip OAuth } From c65e4d764b1047c10beab55f781a79285dc437dc Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 09:34:17 +0100 Subject: [PATCH 051/115] fix: vcl/conditions test after rebase from main --- pkg/commands/vcl/condition/condition_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/vcl/condition/condition_test.go b/pkg/commands/vcl/condition/condition_test.go index c9c25fe09..431746d93 100644 --- a/pkg/commands/vcl/condition/condition_test.go +++ b/pkg/commands/vcl/condition/condition_test.go @@ -257,8 +257,8 @@ SERVICE VERSION NAME STATEMENT TYPE PRIORITY `) + "\n" var listConditionsVerboseOutput = strings.TrimSpace(` -Fastly API token not provided Fastly API endpoint: https://api.fastly.com +Fastly API token provided via config file (profile: user) Service ID (via --service-id): 123 From fe115e2347b276675ab7c8f628ff422cf3b505af Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 12:17:02 +0100 Subject: [PATCH 052/115] refactor(app): hide token message with --quiet --- pkg/app/run.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index e13e3f107..9e2e29a17 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -388,8 +388,10 @@ func shouldSkipSSO(pd *config.Profile, out io.Writer, g *global.Data) bool { if g.Env.UseSSO == "1" { return false // don't skip OAuth } - text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_SSO=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO for the profile you used when authenticating with SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") - text.Break(out) + if !g.Flags.Quiet { + text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_SSO=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO for the profile you used when authenticating with SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") + text.Break(out) + } return true // skip OAuth } return false // don't skip OAuth From 84d33dc080ef54015e1efec904b1f833d519f1de Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 12:17:32 +0100 Subject: [PATCH 053/115] feat: add Important text header --- pkg/app/run.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 9e2e29a17..5ef0534f0 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -419,9 +419,9 @@ func ssoAuthentication( for _, command := range commands { if command.Name() == "authenticate" { if !g.Flags.AutoYes && !g.Flags.NonInteractive { - text.Warning(out, "%s. We need to open your browser to authenticate you.", warningMessage) + text.Important(out, "%s. We need to open your browser to authenticate you.", warningMessage) text.Break(out) - cont, err := text.AskYesNo(out, "Do you want to continue? [y/N]: ", in) + cont, err := text.AskYesNo(out, text.BoldYellow("Do you want to continue? [y/N]: "), in) text.Break(out) if err != nil { return token, tokenSource, err From f7c2b3a23ee1a753cb2fb042dcc00cbaea9d1d7f Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 12:17:47 +0100 Subject: [PATCH 054/115] feat(profile): add SetADefault --- pkg/profile/profile.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index ac135b639..4b45917b2 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -68,6 +68,22 @@ func SetDefault(name string, p config.Profiles) (config.Profiles, bool) { return p, ok } +// SetADefault sets one of the profiles to be the default. +// +// NOTE: This is used by the `authenticate` command. +// The reason it exists is because there could be profiles, but for some reason +// the user has set them all to be not the default. To avoid errors in the CLI +// we require at least one profile to be a default. +func SetADefault(p config.Profiles) (string, config.Profiles) { + var profileName string + for k, v := range p { + profileName = k + v.Default = true + break + } + return profileName, p +} + // Delete removes the named profile from the profile configuration. func Delete(name string, p config.Profiles) bool { var ok bool From a63e88b6a333b9b4280c4c970573a9de32b23cf0 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 12:18:20 +0100 Subject: [PATCH 055/115] test(authenticate): add more test assertions --- pkg/commands/authenticate/auth_test.go | 58 ++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/pkg/commands/authenticate/auth_test.go b/pkg/commands/authenticate/auth_test.go index 4d4420bd0..7e535ffaa 100644 --- a/pkg/commands/authenticate/auth_test.go +++ b/pkg/commands/authenticate/auth_test.go @@ -22,6 +22,7 @@ func TestAuth(t *testing.T) { testutil.TestScenario AuthResult *auth.AuthorizationResult + ConfigFile *config.File ConfigProfile *config.Profile Opener func(input string) error Stdin []string @@ -79,8 +80,12 @@ func TestAuth(t *testing.T) { // Success processing OAuth flow { TestScenario: testutil.TestScenario{ - Args: args("authenticate"), - WantOutput: "Session token (persisted to your local configuration): 123", + Args: args("authenticate"), + WantOutputs: []string{ + "We're going to authenticate the 'user' profile.", + "We need to open your browser to authenticate you.", + "Session token (persisted to your local configuration): 123", + }, }, AuthResult: &auth.AuthorizationResult{ SessionToken: "123", @@ -92,6 +97,35 @@ func TestAuth(t *testing.T) { "Y", // when prompted to open a web browser to start authentication }, }, + // Success processing OAuth flow while setting specific profile (test_user) + { + TestScenario: testutil.TestScenario{ + Args: args("authenticate test_user"), + WantOutputs: []string{ + "We're going to authenticate the 'test_user' profile.", + "We need to open your browser to authenticate you.", + "Session token (persisted to your local configuration): 123", + }, + }, + AuthResult: &auth.AuthorizationResult{ + SessionToken: "123", + }, + ConfigFile: &config.File{ + Profiles: config.Profiles{ + "test_user": &config.Profile{ + Default: true, + Email: "test@example.com", + Token: "mock-token", + }, + }, + }, + ConfigProfile: &config.Profile{ + Token: "123", + }, + Stdin: []string{ + "Y", // when prompted to open a web browser to start authentication + }, + }, } for testcaseIdx := range scenarios { @@ -101,6 +135,10 @@ func TestAuth(t *testing.T) { opts := testutil.NewRunOpts(testcase.Args, &stdout) opts.APIClient = mock.APIClient(testcase.API) + if testcase.ConfigFile != nil { + opts.ConfigFile = *testcase.ConfigFile + } + if testcase.AuthResult != nil { result := make(chan auth.AuthorizationResult) opts.AuthServer = testutil.MockAuthServer{ @@ -110,6 +148,7 @@ func TestAuth(t *testing.T) { result <- *testcase.AuthResult }() } + if testcase.Opener != nil { opts.Opener = testcase.Opener } @@ -164,7 +203,11 @@ func TestAuth(t *testing.T) { } if testcase.ConfigProfile != nil { - userProfile := opts.ConfigFile.Profiles["user"] + profileName := "user" + if len(testcase.Args) > 1 { + profileName = testcase.Args[1] // use the `profile` command argument + } + userProfile := opts.ConfigFile.Profiles[profileName] if userProfile.Token != testcase.ConfigProfile.Token { t.Errorf("want token: %s, got token: %s", testcase.ConfigProfile.Token, userProfile.Token) } @@ -173,7 +216,14 @@ func TestAuth(t *testing.T) { t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.WantError) - testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) + + if testcase.WantOutputs != nil { + for _, s := range testcase.WantOutputs { + testutil.AssertStringContains(t, stdout.String(), s) + } + } else { + testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) + } }) } } From 2bbd7a96086d57c8e88203ed9241981b75f61f13 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 13:41:23 +0100 Subject: [PATCH 056/115] refactor: support profile arg to authenticate command --- pkg/commands/authenticate/root.go | 154 ++++++++++++++++++++---------- pkg/commands/profile/create.go | 4 +- pkg/commands/profile/update.go | 16 ++-- 3 files changed, 112 insertions(+), 62 deletions(-) diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/authenticate/root.go index 9254d92e2..7ac719fef 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/authenticate/root.go @@ -21,17 +21,20 @@ type RootCommand struct { cmd.Base authServer auth.Starter openBrowser func(string) error + profile string // IMPORTANT: The following fields are public to the `profile` subcommands. - // NewProfile indicates if we should create a new profile. - NewProfile bool - // NewProfileName indicates the new profile name. - NewProfileName string + // InvokedFromProfileCreate indicates if we should create a new profile. + InvokedFromProfileCreate bool + // ProfileCreateName indicates the new profile name. + ProfileCreateName string // ProfileDefault indicates if the affected profile should become the default. ProfileDefault bool - // UpdateProfile indicates if we should update a profile. - UpdateProfile bool + // InvokedFromProfileUpdate indicates if we should update a profile. + InvokedFromProfileUpdate bool + // ProfileUpdateName indicates the profile name to update. + ProfileUpdateName string } // NewRootCommand returns a new command registered in the parent. @@ -41,6 +44,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) e c.openBrowser = opener c.Globals = g c.CmdClause = parent.Command("authenticate", "SSO (Single Sign-On) authentication") + c.CmdClause.Arg("profile", "Profile to authenticate (i.e. create/update a token for)").Short('p').StringVar(&c.profile) return &c } @@ -52,7 +56,9 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { // have its own (similar) prompt before invoking this command. So to avoid a // double prompt, the app package will set `SkipAuthPrompt: true`. if !c.Globals.SkipAuthPrompt && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { - text.Warning(out, "We need to open your browser to authenticate you.") + profileName, _ := c.identifyProfileAndFlow() + msg := fmt.Sprintf("We're going to authenticate the '%s' profile", profileName) + text.Important(out, "%s. We need to open your browser to authenticate you.", msg) text.Break(out) cont, err := text.AskYesNo(out, text.BoldYellow("Do you want to continue? [y/N]: "), in) text.Break(out) @@ -128,13 +134,25 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { return nil } -// processProfiles updates the relevant profile with the returned token data. -// -// First it checks the --profile flag and the `profile` fastly.toml field. -// Second it checks to see which profile is currently the default. -// Third it identifies which profile to be modified. -// Fourth it writes the updated in-memory data back to disk. -func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { +// Source enumerates which profile flow to take. +type ProfileFlow uint8 + +const ( + // ProfileNone indicates we need to create a new 'default' profile as no + // profiles currently exist. + ProfileNone ProfileFlow = iota + + // ProfileUpdate indicates we need to create a new profile using details + // passed in either from the `authenticate` or `profile create` command. + ProfileCreate + + // ProfileUpdate indicates we need to update a profile using details passed in + // either from the `authenticate` or `profile update` command. + ProfileUpdate +) + +// identifyProfileAndFlow identifies the profile and the specific workflow. +func (c *RootCommand) identifyProfileAndFlow() (profileName string, flow ProfileFlow) { var profileOverride string switch { case c.Globals.Flags.Profile != "": @@ -143,57 +161,91 @@ func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { profileOverride = c.Globals.Manifest.File.Profile } - profileDefault, _ := profile.Default(c.Globals.Config.Profiles) + currentDefaultProfile, _ := profile.Default(c.Globals.Config.Profiles) + + var newDefaultProfile string + if currentDefaultProfile == "" && len(c.Globals.Config.Profiles) > 0 { + newDefaultProfile, c.Globals.Config.Profiles = profile.SetADefault(c.Globals.Config.Profiles) + } switch { - case noProfilesConfigured(profileOverride, profileDefault): - makeDefault := true - c.Globals.Config.Profiles = createNewProfile(profile.DefaultName, makeDefault, c.Globals.Config.Profiles, ar) - case invokedByProfileCreateCommand(c): - c.Globals.Config.Profiles = createNewProfile(c.NewProfileName, c.ProfileDefault, c.Globals.Config.Profiles, ar) - - // If the user wants the newly created profile to be their new default, then - // we'll call Set for its side effect of resetting all other profiles to have - // their Default field set to false. - if c.ProfileDefault { - if p, ok := profile.SetDefault(c.NewProfileName, c.Globals.Config.Profiles); ok { - c.Globals.Config.Profiles = p - } - } + case profileOverride != "": + return profileOverride, ProfileUpdate + case c.profile != "": + return c.profile, ProfileUpdate + case c.InvokedFromProfileCreate && c.ProfileCreateName != "": + return c.ProfileCreateName, ProfileCreate + case c.InvokedFromProfileUpdate && c.ProfileUpdateName != "": + return c.ProfileUpdateName, ProfileUpdate + case currentDefaultProfile != "": + return currentDefaultProfile, ProfileUpdate + case newDefaultProfile != "": + return newDefaultProfile, ProfileUpdate default: - // Otherwise, edit the existing profile to have the newly generated tokens. - profileName := profileDefault - if profileOverride != "" { - profileName = profileOverride - } - makeDefault := c.ProfileDefault // this is set by `profile update` command. - if !c.UpdateProfile { // if not invoked by `profile update`, then get current `Default` field value - if p := profile.Get(profileName, c.Globals.Config.Profiles); p != nil { - makeDefault = p.Default - } - } - ps, err := editProfile(profileName, makeDefault, c.Globals.Config.Profiles, ar) + return profile.DefaultName, ProfileCreate + } +} + +// processProfiles updates the relevant profile with the returned token data. +// +// First it checks the --profile flag and the `profile` fastly.toml field. +// Second it checks to see which profile is currently the default. +// Third it identifies which profile to be modified. +// Fourth it writes the updated in-memory data back to disk. +func (c *RootCommand) processProfiles(ar auth.AuthorizationResult) error { + profileName, flow := c.identifyProfileAndFlow() + + switch flow { + case ProfileCreate: + c.processCreateProfile(ar, profileName) + case ProfileUpdate: + err := c.processUpdateProfile(ar, profileName) if err != nil { - return err + return fmt.Errorf("failed to update profile: %w", err) } - c.Globals.Config.Profiles = ps } if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { - return fmt.Errorf("error saving config file: %w", err) + return fmt.Errorf("failed to update config file: %w", err) } return nil } -// noProfilesConfigured determines if no profiles have been defined. -func noProfilesConfigured(o, d string) bool { - return o == "" && d == "" +// processCreateProfile handles creating a new profile. +func (c *RootCommand) processCreateProfile(ar auth.AuthorizationResult, profileName string) { + isDefault := true + if c.InvokedFromProfileCreate { + isDefault = c.ProfileDefault + } + + c.Globals.Config.Profiles = createNewProfile(c.ProfileCreateName, isDefault, c.Globals.Config.Profiles, ar) + + // If the user wants the newly created profile to be their new default, then + // we'll call Set for its side effect of resetting all other profiles to have + // their Default field set to false. + if c.ProfileDefault { // this is set by the `profile create` command. + if p, ok := profile.SetDefault(c.ProfileCreateName, c.Globals.Config.Profiles); ok { + c.Globals.Config.Profiles = p + } + } } -// invokedByProfileCreateCommand determines if this command was invoked by the -// `profile create` subcommand. -func invokedByProfileCreateCommand(c *RootCommand) bool { - return c.NewProfile && c.NewProfileName != "" +// processUpdateProfile handles updating a profile. +func (c *RootCommand) processUpdateProfile(ar auth.AuthorizationResult, profileName string) error { + var isDefault bool + if p := profile.Get(profileName, c.Globals.Config.Profiles); p != nil { + isDefault = p.Default + } + if c.InvokedFromProfileUpdate { + isDefault = c.ProfileDefault + } + + ps, err := editProfile(profileName, isDefault, c.Globals.Config.Profiles, ar) + if err != nil { + return err + } + c.Globals.Config.Profiles = ps + return nil } // IMPORTANT: Mutates the config.Profiles map type. diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index d7915b4d4..f8c47a04d 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -83,8 +83,8 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { // // This is so the `authenticate` command will use this information to create // a new 'non-default' profile. - c.authCmd.NewProfile = true - c.authCmd.NewProfileName = c.profile + c.authCmd.InvokedFromProfileCreate = true + c.authCmd.ProfileCreateName = c.profile c.authCmd.ProfileDefault = makeDefault err = c.authCmd.Exec(in, out) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index a28e32325..bb9c40ab3 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -75,7 +75,7 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { return fmt.Errorf("failed to update token: %w", err) } } else if makeDefault { // only set default if not updating the token (as updating the token already handles setting the default) - err := c.updateDefault(profileName, in, out) + err := c.updateDefault(profileName, out) if err != nil { return fmt.Errorf("failed to update token: %w", err) } @@ -139,14 +139,12 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con } if useOAuthFlow { - // IMPORTANT: We need to temporarily set profile override. + // IMPORTANT: We need to set profile fields for authenticate command. // - // This is so the `authenticate` command will use the override, rather than - // incorrectly updating the 'default' profile. We also need to pass through - // an indicator that an existing profile should be updated and also whether - // that existing profile should now be made the default. - c.Globals.Flags.Profile = profileName - c.authCmd.UpdateProfile = true + // This is so the `authenticate` command will use this information to update + // the specific profile. + c.authCmd.InvokedFromProfileUpdate = true + c.authCmd.ProfileUpdateName = profileName c.authCmd.ProfileDefault = makeDefault err := c.authCmd.Exec(in, out) @@ -169,7 +167,7 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con return nil } -func (c *UpdateCommand) updateDefault(profileName string, in io.Reader, out io.Writer) error { +func (c *UpdateCommand) updateDefault(profileName string, out io.Writer) error { p, ok := profile.SetDefault(profileName, c.Globals.Config.Profiles) if !ok { return errors.New("failed to update the profile's default field") From a21fb9a2fa9898bfcb0274f50820427f08699af7 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 14:25:34 +0100 Subject: [PATCH 057/115] refactor: rename authenticate command to sso --- pkg/app/commands.go | 18 +++++++++++++----- pkg/app/run_test.go | 2 +- pkg/commands/authenticate/doc.go | 3 --- pkg/commands/profile/create.go | 6 +++--- pkg/commands/profile/update.go | 6 +++--- pkg/commands/sso/doc.go | 3 +++ pkg/commands/{authenticate => sso}/root.go | 4 ++-- .../auth_test.go => sso/sso_test.go} | 16 ++++++++-------- 8 files changed, 33 insertions(+), 25 deletions(-) delete mode 100644 pkg/commands/authenticate/doc.go create mode 100644 pkg/commands/sso/doc.go rename pkg/commands/{authenticate => sso}/root.go (98%) rename pkg/commands/{authenticate/auth_test.go => sso/sso_test.go} (95%) diff --git a/pkg/app/commands.go b/pkg/app/commands.go index 69b2b0e33..58a04eb81 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -6,7 +6,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/acl" "github.com/fastly/cli/pkg/commands/aclentry" - "github.com/fastly/cli/pkg/commands/authenticate" "github.com/fastly/cli/pkg/commands/authtoken" "github.com/fastly/cli/pkg/commands/backend" "github.com/fastly/cli/pkg/commands/compute" @@ -60,6 +59,7 @@ import ( "github.com/fastly/cli/pkg/commands/serviceauth" "github.com/fastly/cli/pkg/commands/serviceversion" "github.com/fastly/cli/pkg/commands/shellcomplete" + "github.com/fastly/cli/pkg/commands/sso" "github.com/fastly/cli/pkg/commands/stats" tlsconfig "github.com/fastly/cli/pkg/commands/tls/config" tlscustom "github.com/fastly/cli/pkg/commands/tls/custom" @@ -88,6 +88,15 @@ func defineCommands( opts RunOpts, ) []cmd.Command { shellcompleteCmdRoot := shellcomplete.NewRootCommand(app, g) + + // NOTE: The order commands are created are the order they appear in 'help'. + // But because we need to pass the SSO command into the profile commands, it + // means the SSO command must be created _before_ the profile commands. This + // messes up the order of the commands in the `--help` output. So to make the + // placement of the `sso` subcommand not look too odd we place it at the + // beginning of the list of commands. + ssoCmdRoot := sso.NewRootCommand(app, g, opts.Opener, opts.AuthServer) + aclCmdRoot := acl.NewRootCommand(app, g) aclCreate := acl.NewCreateCommand(aclCmdRoot.CmdClause, g, m) aclDelete := acl.NewDeleteCommand(aclCmdRoot.CmdClause, g, m) @@ -100,7 +109,6 @@ func defineCommands( aclEntryDescribe := aclentry.NewDescribeCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryList := aclentry.NewListCommand(aclEntryCmdRoot.CmdClause, g, m) aclEntryUpdate := aclentry.NewUpdateCommand(aclEntryCmdRoot.CmdClause, g, m) - authenticateCmdRoot := authenticate.NewRootCommand(app, g, opts.Opener, opts.AuthServer) authtokenCmdRoot := authtoken.NewRootCommand(app, g) authtokenCreate := authtoken.NewCreateCommand(authtokenCmdRoot.CmdClause, g, m) authtokenDelete := authtoken.NewDeleteCommand(authtokenCmdRoot.CmdClause, g, m) @@ -335,12 +343,12 @@ func defineCommands( popCmdRoot := pop.NewRootCommand(app, g) productsCmdRoot := products.NewRootCommand(app, g, m) profileCmdRoot := profile.NewRootCommand(app, g) - profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, authenticateCmdRoot) + profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, ssoCmdRoot) profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, g) profileList := profile.NewListCommand(profileCmdRoot.CmdClause, g) profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, g) profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, g) - profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, authenticateCmdRoot) + profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, ssoCmdRoot) purgeCmdRoot := purge.NewRootCommand(app, g, m) rateLimitCmdRoot := ratelimit.NewRootCommand(app, g) rateLimitCreate := ratelimit.NewCreateCommand(rateLimitCmdRoot.CmdClause, g, m) @@ -467,7 +475,6 @@ func defineCommands( aclEntryDescribe, aclEntryList, aclEntryUpdate, - authenticateCmdRoot, authtokenCmdRoot, authtokenCreate, authtokenDelete, @@ -747,6 +754,7 @@ func defineCommands( serviceVersionList, serviceVersionLock, serviceVersionUpdate, + ssoCmdRoot, statsCmdRoot, statsHistorical, statsRealtime, diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index 9388666dd..663c5b3e1 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -57,9 +57,9 @@ complete -F _fastly_bash_autocomplete fastly Name: "shell evaluate completion options", Args: args("--completion-bash"), WantOutput: `help +sso acl acl-entry -authenticate auth-token backend compute diff --git a/pkg/commands/authenticate/doc.go b/pkg/commands/authenticate/doc.go deleted file mode 100644 index e688c1ee6..000000000 --- a/pkg/commands/authenticate/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package authenticate contains commands to authenticate with Fastly and to -// acquire a temporary API token, which will be auto-rotated. -package authenticate diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index f8c47a04d..2c5be708c 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -13,7 +13,7 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/commands/authenticate" + "github.com/fastly/cli/pkg/commands/sso" "github.com/fastly/cli/pkg/config" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" @@ -24,7 +24,7 @@ import ( // CreateCommand represents a Kingpin command. type CreateCommand struct { cmd.Base - authCmd *authenticate.RootCommand + authCmd *sso.RootCommand automationToken bool clientFactory APIClientFactory @@ -32,7 +32,7 @@ type CreateCommand struct { } // NewCreateCommand returns a new command registered in the parent. -func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *authenticate.RootCommand) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *sso.RootCommand) *CreateCommand { var c CreateCommand c.Globals = g c.authCmd = authCmd diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index bb9c40ab3..2d04c5952 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -9,7 +9,7 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/cmd" - "github.com/fastly/cli/pkg/commands/authenticate" + "github.com/fastly/cli/pkg/commands/sso" "github.com/fastly/cli/pkg/config" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" @@ -25,7 +25,7 @@ type APIClientFactory func(token, endpoint string, debugMode bool) (api.Interfac // UpdateCommand represents a Kingpin command. type UpdateCommand struct { cmd.Base - authCmd *authenticate.RootCommand + authCmd *sso.RootCommand automationToken bool clientFactory APIClientFactory @@ -33,7 +33,7 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *authenticate.RootCommand) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *sso.RootCommand) *UpdateCommand { var c UpdateCommand c.Globals = g c.authCmd = authCmd diff --git a/pkg/commands/sso/doc.go b/pkg/commands/sso/doc.go new file mode 100644 index 000000000..14585f77e --- /dev/null +++ b/pkg/commands/sso/doc.go @@ -0,0 +1,3 @@ +// Package sso contains commands to authenticate with Fastly and to acquire a +// temporary API token, which will be auto-rotated using an access/refresh token. +package sso diff --git a/pkg/commands/authenticate/root.go b/pkg/commands/sso/root.go similarity index 98% rename from pkg/commands/authenticate/root.go rename to pkg/commands/sso/root.go index 7ac719fef..8cc7af9a5 100644 --- a/pkg/commands/authenticate/root.go +++ b/pkg/commands/sso/root.go @@ -1,4 +1,4 @@ -package authenticate +package sso import ( "errors" @@ -43,7 +43,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) e c.authServer = authServer c.openBrowser = opener c.Globals = g - c.CmdClause = parent.Command("authenticate", "SSO (Single Sign-On) authentication") + c.CmdClause = parent.Command("sso", "Single Sign-On authentication") c.CmdClause.Arg("profile", "Profile to authenticate (i.e. create/update a token for)").Short('p').StringVar(&c.profile) return &c } diff --git a/pkg/commands/authenticate/auth_test.go b/pkg/commands/sso/sso_test.go similarity index 95% rename from pkg/commands/authenticate/auth_test.go rename to pkg/commands/sso/sso_test.go index 7e535ffaa..fec7d0a78 100644 --- a/pkg/commands/authenticate/auth_test.go +++ b/pkg/commands/sso/sso_test.go @@ -1,4 +1,4 @@ -package authenticate_test +package sso_test import ( "bytes" @@ -16,7 +16,7 @@ import ( "github.com/fastly/cli/pkg/testutil" ) -func TestAuth(t *testing.T) { +func TestSSO(t *testing.T) { args := testutil.Args type ts struct { testutil.TestScenario @@ -31,7 +31,7 @@ func TestAuth(t *testing.T) { // User cancels authentication prompt { TestScenario: testutil.TestScenario{ - Args: args("authenticate"), + Args: args("sso"), WantError: "user cancelled execution", }, Stdin: []string{ @@ -41,7 +41,7 @@ func TestAuth(t *testing.T) { // Error opening web browser { TestScenario: testutil.TestScenario{ - Args: args("authenticate"), + Args: args("sso"), WantError: "failed to open web browser", }, Opener: func(input string) error { @@ -54,7 +54,7 @@ func TestAuth(t *testing.T) { // Error processing OAuth flow (error encountered) { TestScenario: testutil.TestScenario{ - Args: args("authenticate"), + Args: args("sso"), WantError: "failed to authorize: no authorization code returned", }, AuthResult: &auth.AuthorizationResult{ @@ -67,7 +67,7 @@ func TestAuth(t *testing.T) { // Error processing OAuth flow (empty SessionToken field) { TestScenario: testutil.TestScenario{ - Args: args("authenticate"), + Args: args("sso"), WantError: "failed to authorize: no session token", }, AuthResult: &auth.AuthorizationResult{ @@ -80,7 +80,7 @@ func TestAuth(t *testing.T) { // Success processing OAuth flow { TestScenario: testutil.TestScenario{ - Args: args("authenticate"), + Args: args("sso"), WantOutputs: []string{ "We're going to authenticate the 'user' profile.", "We need to open your browser to authenticate you.", @@ -100,7 +100,7 @@ func TestAuth(t *testing.T) { // Success processing OAuth flow while setting specific profile (test_user) { TestScenario: testutil.TestScenario{ - Args: args("authenticate test_user"), + Args: args("sso test_user"), WantOutputs: []string{ "We're going to authenticate the 'test_user' profile.", "We need to open your browser to authenticate you.", From 24ea69d72d880b98a3037099abd2fd64a37c4f10 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 15:38:22 +0100 Subject: [PATCH 058/115] doc: rename DEVELOP to DEVELOPMENT --- DEVELOP.md => DEVELOPMENT.md | 0 README.md | 7 ++++--- 2 files changed, 4 insertions(+), 3 deletions(-) rename DEVELOP.md => DEVELOPMENT.md (100%) diff --git a/DEVELOP.md b/DEVELOPMENT.md similarity index 100% rename from DEVELOP.md rename to DEVELOPMENT.md diff --git a/README.md b/README.md index d97221007..34bc7f7ae 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,12 @@ ## Quick links + - [Installation](https://developer.fastly.com/learning/tools/cli#installing) - [Shell auto-completion](https://developer.fastly.com/learning/tools/cli#shell-auto-completion) - [Configuring](https://developer.fastly.com/learning/tools/cli#configuring) - [Commands](https://developer.fastly.com/reference/cli/#command-groups) -- [Development](DEVELOP.md) +- [Development](DEVELOPMENT.md) - [Testing](TESTING.md) - [Documentation](DOCUMENTATION.md) @@ -27,8 +28,6 @@ Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) If you encounter any non-security-related bug or unexpected behavior, please [file an issue][bug] using the bug report template. -[bug]: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md - Please also check the [CHANGELOG](./CHANGELOG.md) for any breaking-changes or migration guidance. ### Security issues @@ -38,3 +37,5 @@ Please see our [SECURITY.md](SECURITY.md) for guidance on reporting security-rel ## License [Apache 2.0](LICENSE). + +[bug]: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md From acf1bb5c4bb37ad0687395a6a20af075c249e3bb Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 15:39:09 +0100 Subject: [PATCH 059/115] fix: split command name to avoid arguments being included --- pkg/app/run.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 5ef0534f0..8a44aa127 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -417,7 +417,8 @@ func ssoAuthentication( g *global.Data, ) (string, lookup.Source, error) { for _, command := range commands { - if command.Name() == "authenticate" { + commandName := strings.Split(command.Name(), " ")[0] + if commandName == "sso" { if !g.Flags.AutoYes && !g.Flags.NonInteractive { text.Important(out, "%s. We need to open your browser to authenticate you.", warningMessage) text.Break(out) From 2405855048e9d4d1ccba3a537e562c9ff27e93b2 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 15:39:45 +0100 Subject: [PATCH 060/115] fix: use correct profile name after authentication --- pkg/commands/sso/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index 8cc7af9a5..533f5625e 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -218,7 +218,7 @@ func (c *RootCommand) processCreateProfile(ar auth.AuthorizationResult, profileN isDefault = c.ProfileDefault } - c.Globals.Config.Profiles = createNewProfile(c.ProfileCreateName, isDefault, c.Globals.Config.Profiles, ar) + c.Globals.Config.Profiles = createNewProfile(profileName, isDefault, c.Globals.Config.Profiles, ar) // If the user wants the newly created profile to be their new default, then // we'll call Set for its side effect of resetting all other profiles to have From 3a20b5240162d6f6b08dac2fbe1005e55a7efa37 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 13 Sep 2023 15:40:09 +0100 Subject: [PATCH 061/115] refactor: rename all instances of authenticate to sso --- pkg/app/run.go | 8 ++++---- pkg/commands/profile/create.go | 4 ++-- pkg/commands/profile/update.go | 4 ++-- pkg/commands/sso/root.go | 14 +++++++------- pkg/global/global.go | 2 +- pkg/profile/profile.go | 9 +++++---- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 8a44aa127..7bd521f31 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -197,7 +197,7 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // validate if it has expired, and if it has we will attempt to refresh it. // // If both the access token and the refresh token has expired we will trigger -// the `fastly authenticate` command to execute. +// the `fastly sso` command to execute. // // Either way, the CLI config is updated to reflect the token that was either // refreshed or regenerated from the authentication process. @@ -366,7 +366,7 @@ func checkProfileToken( if !ok { return tokenSource, warningMessage, fsterr.RemediationError{ Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), - Remediation: "Run `fastly authenticate` to retry.", + Remediation: "Run `fastly sso` to retry.", } } g.Config.Profiles = ps @@ -432,7 +432,7 @@ func ssoAuthentication( } } - g.SkipAuthPrompt = true // skip the same prompt in `authenticate` command flow + g.SkipAuthPrompt = true // skip the same prompt in `sso` command flow err := command.Exec(in, out) if err != nil { return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) @@ -571,7 +571,7 @@ func commandRequiresToken(command string) bool { } command = strings.Split(command, " ")[0] switch command { - case "authenticate", "config", "profile", "update", "version": + case "config", "profile", "sso", "update", "version": return false } return true diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 2c5be708c..ace658d2a 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -79,9 +79,9 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { } } if useOAuthFlow { - // IMPORTANT: We need to set profile fields for authenticate command. + // IMPORTANT: We need to set profile fields for `sso` command. // - // This is so the `authenticate` command will use this information to create + // This is so the `sso` command will use this information to create // a new 'non-default' profile. c.authCmd.InvokedFromProfileCreate = true c.authCmd.ProfileCreateName = c.profile diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 2d04c5952..09d7121e9 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -139,9 +139,9 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con } if useOAuthFlow { - // IMPORTANT: We need to set profile fields for authenticate command. + // IMPORTANT: We need to set profile fields for `sso` command. // - // This is so the `authenticate` command will use this information to update + // This is so the `sso` command will use this information to update // the specific profile. c.authCmd.InvokedFromProfileUpdate = true c.authCmd.ProfileUpdateName = profileName diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index 533f5625e..69fae3832 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -51,10 +51,10 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) e // Exec implements the command interface. func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { // We need to prompt the user, so they know we're about to open their web - // browser, but we also need to handle the scenario where the `authenticate` - // command is invoked indirectly via ../../app/run.go as that package will - // have its own (similar) prompt before invoking this command. So to avoid a - // double prompt, the app package will set `SkipAuthPrompt: true`. + // browser, but we also need to handle the scenario where the `sso` command is + // invoked indirectly via ../../app/run.go as that package will have its own + // (similar) prompt before invoking this command. So to avoid a double prompt, + // the app package will set `SkipAuthPrompt: true`. if !c.Globals.SkipAuthPrompt && !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { profileName, _ := c.identifyProfileAndFlow() msg := fmt.Sprintf("We're going to authenticate the '%s' profile", profileName) @@ -143,11 +143,11 @@ const ( ProfileNone ProfileFlow = iota // ProfileUpdate indicates we need to create a new profile using details - // passed in either from the `authenticate` or `profile create` command. + // passed in either from the `sso` or `profile create` command. ProfileCreate // ProfileUpdate indicates we need to update a profile using details passed in - // either from the `authenticate` or `profile update` command. + // either from the `sso` or `profile update` command. ProfileUpdate ) @@ -287,7 +287,7 @@ func editProfile(profileName string, makeDefault bool, p config.Profiles, ar aut if !ok { return ps, fsterr.RemediationError{ Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), - Remediation: "Run `fastly authenticate` to retry.", + Remediation: "Run `fastly sso` to retry.", } } return ps, nil diff --git a/pkg/global/global.go b/pkg/global/global.go index a17c7a111..be2e89e3d 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -44,7 +44,7 @@ type Data struct { Manifest manifest.Data // Output is the output for displaying information (typically os.Stdout) Output io.Writer - // SkipAuthPrompt is used to indicate to the `authenticate` command that the + // SkipAuthPrompt is used to indicate to the `sso` command that the // interactive prompt can be skipped. This is for scenarios where the command // is executed directly by the user. SkipAuthPrompt bool diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index 4b45917b2..e64bb5863 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -70,10 +70,11 @@ func SetDefault(name string, p config.Profiles) (config.Profiles, bool) { // SetADefault sets one of the profiles to be the default. // -// NOTE: This is used by the `authenticate` command. -// The reason it exists is because there could be profiles, but for some reason -// the user has set them all to be not the default. To avoid errors in the CLI -// we require at least one profile to be a default. +// NOTE: This is used by the `sso` command. +// The reason it exists is because there could be profiles that for some reason +// the user has set them all to not be a default. So to avoid errors in the CLI +// we require at least one profile to be a default and this function makes it +// easy to just pick the first profile and generically set it as the default. func SetADefault(p config.Profiles) (string, config.Profiles) { var profileName string for k, v := range p { From 4a365a3c911e58b1e52d6ceb907555dcf87bdf67 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 11:11:03 +0100 Subject: [PATCH 062/115] refactor(app): reword instructions for SSO opt-in --- pkg/app/run.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 7bd521f31..3e1e2d6a3 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -282,7 +282,7 @@ func checkProfileToken( } // Allow user to opt-in to SSO/OAuth so they can replace their long-lived token. - if shouldSkipSSO(profileData, out, g) { + if shouldSkipSSO(profileName, profileData, out, g) { return tokenSource, warningMessage, nil } @@ -383,13 +383,13 @@ func checkProfileToken( // shouldSkipSSO identifies if a config is a pre-v4 config and, if it is, // informs the user how they can use the OAuth flow. It checks if the OAuth // environment variable has been set and enables the OAuth flow if so. -func shouldSkipSSO(pd *config.Profile, out io.Writer, g *global.Data) bool { +func shouldSkipSSO(profileName string, pd *config.Profile, out io.Writer, g *global.Data) bool { if noOAuthToken(pd) { if g.Env.UseSSO == "1" { return false // don't skip OAuth } if !g.Flags.Quiet { - text.Info(out, "Your token doesn't appear to have been generated using Fastly SSO (Single Sign-On) which offers more security and convenience. To use SSO, you'll need to opt into it by setting the environment variable `FASTLY_USE_SSO=1`. This variable only needs to be set once, then all future CLI invocations will default to SSO for the profile you used when authenticating with SSO (--token and FASTLY_API_TOKEN can still be used as overrides).") + text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).", profileName) text.Break(out) } return true // skip OAuth From 5293e696bd4906b629152fbaa56ddb47455adb01 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 12:14:31 +0100 Subject: [PATCH 063/115] refactor(profile/update): avoid excessive config writes --- pkg/commands/profile/update.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 09d7121e9..9a0414b45 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -147,6 +147,8 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con c.authCmd.ProfileUpdateName = profileName c.authCmd.ProfileDefault = makeDefault + // NOTE: The `sso` command already handles writing config back to disk. + // So unlike `c.staticTokenFlow` (below) we don't have to do that here. err := c.authCmd.Exec(in, out) if err != nil { return fmt.Errorf("failed to authenticate: %w", err) @@ -156,12 +158,11 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con if err := c.staticTokenFlow(profileName, makeDefault, p, in, out); err != nil { return fmt.Errorf("failed to process the static token flow: %w", err) } - } - - // Write the in-memory representation back to disk. - if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { - c.Globals.ErrLog.Add(err) - return fmt.Errorf("error saving config file: %w", err) + // Write the in-memory representation back to disk. + if err := c.Globals.Config.Write(c.Globals.ConfigPath); err != nil { + c.Globals.ErrLog.Add(err) + return fmt.Errorf("error saving config file: %w", err) + } } return nil From d2b26d6d4a9659c903cc00d10b40390a4f9c7c62 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 12:14:54 +0100 Subject: [PATCH 064/115] refactor(sso): change text function depending on how command is invoked --- pkg/commands/sso/root.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index 69fae3832..f5f7b7c56 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -130,7 +130,11 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { return fmt.Errorf("failed to process profile data: %w", err) } - text.Success(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) + textFn := text.Success + if c.InvokedFromProfileCreate || c.InvokedFromProfileUpdate { + textFn = text.Info + } + textFn(out, "Session token (persisted to your local configuration): %s", ar.SessionToken) return nil } From 10e7b33c9cda02ea3a15323788574ae35d703cbb Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 16:55:01 +0100 Subject: [PATCH 065/115] feat(sso): validate azp/aud claims --- pkg/auth/auth.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index ff54bde26..93d7016e3 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -128,6 +128,39 @@ func (s *Server) HandleCallback() http.HandlerFunc { return } + azp, ok := claims["azp"] + if !ok { + s.Result <- AuthorizationResult{ + Err: errors.New("failed to extract azp from JWT claims"), + } + return + } + if azp != ClientID { + if !ok { + s.Result <- AuthorizationResult{ + Err: fmt.Errorf("failed to match expected azp: %s", azp), + } + return + } + } + + aud, ok := claims["aud"] + if !ok { + s.Result <- AuthorizationResult{ + Err: errors.New("failed to extract aud from JWT claims"), + } + return + } + + if aud != s.APIEndpoint { + if !ok { + s.Result <- AuthorizationResult{ + Err: fmt.Errorf("failed to match expected aud: %s", s.APIEndpoint), + } + return + } + } + email, ok := claims["email"] if !ok { s.Result <- AuthorizationResult{ From 3cf22978faa66ff4f664a6393cadf8a0a26b6c71 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 17:15:23 +0100 Subject: [PATCH 066/115] fix: resolve linter feedback --- pkg/auth/auth.go | 2 +- pkg/commands/profile/update.go | 4 ++-- pkg/commands/sso/root.go | 4 ++-- pkg/testutil/args.go | 6 ++++++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 93d7016e3..1d5cf760a 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -238,7 +238,7 @@ func GenURL(accountEndpoint, apiEndpoint string, verifier *oidc.S256Verifier) (s return authorizationURL, nil } -// GenJWT constructs and calls the token_endpoint path, returning a JWT +// GetJWT constructs and calls the token_endpoint path, returning a JWT // containing the access and refresh tokens and associated TTLs. func GetJWT(accountEndpoint, codeVerifier, authorizationCode string) (JWT, error) { path := "/realms/fastly/protocol/openid-connect/token" diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 9a0414b45..5a4110d82 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -75,7 +75,7 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { return fmt.Errorf("failed to update token: %w", err) } } else if makeDefault { // only set default if not updating the token (as updating the token already handles setting the default) - err := c.updateDefault(profileName, out) + err := c.updateDefault(profileName) if err != nil { return fmt.Errorf("failed to update token: %w", err) } @@ -168,7 +168,7 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con return nil } -func (c *UpdateCommand) updateDefault(profileName string, out io.Writer) error { +func (c *UpdateCommand) updateDefault(profileName string) error { p, ok := profile.SetDefault(profileName, c.Globals.Config.Profiles) if !ok { return errors.New("failed to update the profile's default field") diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index f5f7b7c56..a0e3f2698 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -138,7 +138,7 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { return nil } -// Source enumerates which profile flow to take. +// ProfileFlow enumerates which profile flow to take. type ProfileFlow uint8 const ( @@ -146,7 +146,7 @@ const ( // profiles currently exist. ProfileNone ProfileFlow = iota - // ProfileUpdate indicates we need to create a new profile using details + // ProfileCreate indicates we need to create a new profile using details // passed in either from the `sso` or `profile create` command. ProfileCreate diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index ac84fcbe0..4c0db2cdb 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -63,22 +63,27 @@ type MockAuthServer struct { Result chan auth.AuthorizationResult } +// GetResult returns the results channel func (s MockAuthServer) GetResult() chan auth.AuthorizationResult { return s.Result } +// SetAccountEndpoint sets the account endpoint. func (s MockAuthServer) SetAccountEndpoint(_ string) { // no-op } +// SetEndpoint sets the API endpoint. func (s MockAuthServer) SetAPIEndpoint(_ string) { // no-op } +// SetVerifier sets the code verifier. func (s MockAuthServer) SetVerifier(_ *oidc.S256Verifier) { // no-op } +// Start starts a local server for handling authentication processing. func (s MockAuthServer) Start() error { return nil // no-op } @@ -120,6 +125,7 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { } } +// TokenProfile generates a mock profile token. func TokenProfile() config.Profiles { return config.Profiles{ // IMPORTANT: Tests mock the token to prevent runtime panics. From 6177f5f2e3447f2fdc3ef286cf706279c2c4ede2 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 17:33:54 +0100 Subject: [PATCH 067/115] refactor: rename testcase field --- pkg/commands/sso/sso_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index fec7d0a78..07e00b771 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -21,11 +21,11 @@ func TestSSO(t *testing.T) { type ts struct { testutil.TestScenario - AuthResult *auth.AuthorizationResult - ConfigFile *config.File - ConfigProfile *config.Profile - Opener func(input string) error - Stdin []string + AuthResult *auth.AuthorizationResult + ConfigFile *config.File + ExpectedConfigProfile *config.Profile + Opener func(input string) error + Stdin []string } scenarios := []ts{ // User cancels authentication prompt @@ -90,7 +90,7 @@ func TestSSO(t *testing.T) { AuthResult: &auth.AuthorizationResult{ SessionToken: "123", }, - ConfigProfile: &config.Profile{ + ExpectedConfigProfile: &config.Profile{ Token: "123", }, Stdin: []string{ @@ -119,7 +119,7 @@ func TestSSO(t *testing.T) { }, }, }, - ConfigProfile: &config.Profile{ + ExpectedConfigProfile: &config.Profile{ Token: "123", }, Stdin: []string{ @@ -202,14 +202,14 @@ func TestSSO(t *testing.T) { err = app.Run(opts) } - if testcase.ConfigProfile != nil { + if testcase.ExpectedConfigProfile != nil { profileName := "user" if len(testcase.Args) > 1 { profileName = testcase.Args[1] // use the `profile` command argument } userProfile := opts.ConfigFile.Profiles[profileName] - if userProfile.Token != testcase.ConfigProfile.Token { - t.Errorf("want token: %s, got token: %s", testcase.ConfigProfile.Token, userProfile.Token) + if userProfile.Token != testcase.ExpectedConfigProfile.Token { + t.Errorf("want token: %s, got token: %s", testcase.ExpectedConfigProfile.Token, userProfile.Token) } } From e8b55029276d6fed2c6003409be56bd86bb4c014 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 14 Sep 2023 18:07:43 +0100 Subject: [PATCH 068/115] test: add more tests to validate pre-command token processing --- pkg/commands/sso/sso_test.go | 109 +++++++++++++++++++++++++++-- pkg/commands/whoami/whoami_test.go | 44 ++---------- pkg/testutil/api.go | 38 ++++++++++ 3 files changed, 145 insertions(+), 46 deletions(-) diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index 07e00b771..dbceb0054 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/config" @@ -24,11 +25,12 @@ func TestSSO(t *testing.T) { AuthResult *auth.AuthorizationResult ConfigFile *config.File ExpectedConfigProfile *config.Profile + HTTPClient api.HTTPClient Opener func(input string) error Stdin []string } scenarios := []ts{ - // User cancels authentication prompt + // 0. User cancels authentication prompt { TestScenario: testutil.TestScenario{ Args: args("sso"), @@ -38,7 +40,7 @@ func TestSSO(t *testing.T) { "N", // when prompted to open a web browser to start authentication }, }, - // Error opening web browser + // 1. Error opening web browser { TestScenario: testutil.TestScenario{ Args: args("sso"), @@ -51,7 +53,7 @@ func TestSSO(t *testing.T) { "Y", // when prompted to open a web browser to start authentication }, }, - // Error processing OAuth flow (error encountered) + // 2. Error processing OAuth flow (error encountered) { TestScenario: testutil.TestScenario{ Args: args("sso"), @@ -64,7 +66,7 @@ func TestSSO(t *testing.T) { "Y", // when prompted to open a web browser to start authentication }, }, - // Error processing OAuth flow (empty SessionToken field) + // 3. Error processing OAuth flow (empty SessionToken field) { TestScenario: testutil.TestScenario{ Args: args("sso"), @@ -77,7 +79,7 @@ func TestSSO(t *testing.T) { "Y", // when prompted to open a web browser to start authentication }, }, - // Success processing OAuth flow + // 4. Success processing OAuth flow { TestScenario: testutil.TestScenario{ Args: args("sso"), @@ -97,7 +99,7 @@ func TestSSO(t *testing.T) { "Y", // when prompted to open a web browser to start authentication }, }, - // Success processing OAuth flow while setting specific profile (test_user) + // 5. Success processing OAuth flow while setting specific profile (test_user) { TestScenario: testutil.TestScenario{ Args: args("sso test_user"), @@ -126,6 +128,97 @@ func TestSSO(t *testing.T) { "Y", // when prompted to open a web browser to start authentication }, }, + // NOTE: The following tests indirectly validate our `app.Run()` logic. + // Specifically the processing of the token before invoking the subcommand. + // It allows us to check that the `sso` command is invoked when expected. + // + // 6. Success processing `whoami` command. + // We configure a non-SSO token so we can validate the INFO message. + // Otherwise no OAuth flow is happening here. + { + TestScenario: testutil.TestScenario{ + Args: args("whoami"), + WantOutputs: []string{ + "is not a Fastly SSO (Single Sign-On) generated token", + "Alice Programmer ", + }, + }, + ConfigFile: &config.File{ + Profiles: config.Profiles{ + "user": &config.Profile{ + Default: true, + Email: "test@example.com", + Token: "mock-token", + }, + }, + }, + ExpectedConfigProfile: &config.Profile{ + Token: "mock-token", + }, + HTTPClient: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), + }, + // 7. Success processing `whoami` command. + // We set an SSO token that has expired. + // This allows us to validate the output message about expiration. + // We don't respond "Y" to prompt to reauthenticate. + // But we've mocked the request to succeeds still so it doesn't matter. + { + TestScenario: testutil.TestScenario{ + Args: args("whoami"), + WantOutputs: []string{ + "Your access token has expired and so has your refresh token.", + "Alice Programmer ", + }, + }, + ConfigFile: &config.File{ + Profiles: config.Profiles{ + "user": &config.Profile{ + AccessTokenCreated: time.Now().Add(-(time.Duration(300) * time.Second)).Unix(), // 5 mins ago + Default: true, + Email: "test@example.com", + Token: "mock-token", + }, + }, + }, + ExpectedConfigProfile: &config.Profile{ + Token: "mock-token", + }, + HTTPClient: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), + }, + // 8. Success processing OAuth flow via `whoami` command + // We set an SSO token that has expired. + // This allows us to validate the output messages. + { + TestScenario: testutil.TestScenario{ + Args: args("whoami"), + WantOutputs: []string{ + "Your access token has expired and so has your refresh token.", + "Starting a local server to handle the authentication flow.", + "Session token (persisted to your local configuration): 123", + "Alice Programmer ", + }, + }, + AuthResult: &auth.AuthorizationResult{ + SessionToken: "123", + }, + ConfigFile: &config.File{ + Profiles: config.Profiles{ + "user": &config.Profile{ + AccessTokenCreated: time.Now().Add(-(time.Duration(300) * time.Second)).Unix(), // 5 mins ago + Default: true, + Email: "test@example.com", + Token: "mock-token", + }, + }, + }, + ExpectedConfigProfile: &config.Profile{ + Token: "123", + }, + HTTPClient: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), + Stdin: []string{ + "Y", // when prompted to open a web browser to start authentication + }, + }, } for testcaseIdx := range scenarios { @@ -135,6 +228,10 @@ func TestSSO(t *testing.T) { opts := testutil.NewRunOpts(testcase.Args, &stdout) opts.APIClient = mock.APIClient(testcase.API) + if testcase.HTTPClient != nil { + opts.HTTPClient = testcase.HTTPClient + } + if testcase.ConfigFile != nil { opts.ConfigFile = *testcase.ConfigFile } diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index f4b92d57d..13a68c39b 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -2,7 +2,6 @@ package whoami_test import ( "bytes" - "encoding/json" "errors" "fmt" "net/http" @@ -12,7 +11,6 @@ import ( "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/app" - "github.com/fastly/cli/pkg/commands/whoami" "github.com/fastly/cli/pkg/config" "github.com/fastly/cli/pkg/env" "github.com/fastly/cli/pkg/testutil" @@ -31,13 +29,13 @@ func TestWhoami(t *testing.T) { { name: "basic response", args: args("whoami"), - client: verifyClient(basicResponse), + client: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), wantOutput: basicOutput, }, { name: "basic response verbose", args: args("whoami -v"), - client: verifyClient(basicResponse), + client: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), wantOutput: basicOutputVerbose, }, { @@ -55,7 +53,7 @@ func TestWhoami(t *testing.T) { { name: "alternative endpoint from flag", args: args("whoami --endpoint=https://staging.fastly.com -v"), - client: verifyClient(basicResponse), + client: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, "Fastly API endpoint: https://api.fastly.com", "Fastly API endpoint (via --endpoint): https://staging.fastly.com", @@ -65,7 +63,7 @@ func TestWhoami(t *testing.T) { name: "alternative endpoint from environment", args: args("whoami -v"), env: config.Environment{Endpoint: "https://alternative.example.com"}, - client: verifyClient(basicResponse), + client: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, "Fastly API endpoint: https://api.fastly.com", fmt.Sprintf("Fastly API endpoint (via %s): https://alternative.example.com", env.Endpoint), @@ -86,17 +84,6 @@ func TestWhoami(t *testing.T) { } } -type verifyClient whoami.VerifyResponse - -func (c verifyClient) Do(*http.Request) (*http.Response, error) { - rec := httptest.NewRecorder() - err := json.NewEncoder(rec).Encode(whoami.VerifyResponse(c)) - if err != nil { - return nil, fmt.Errorf("failed to encode response into json: %w", err) - } - return rec.Result(), nil -} - type codeClient struct { code int } @@ -115,29 +102,6 @@ func (c errorClient) Do(*http.Request) (*http.Response, error) { return nil, c.err } -var basicResponse = whoami.VerifyResponse{ - Customer: whoami.Customer{ - ID: "abc", - Name: "Computer Company", - }, - User: whoami.User{ - ID: "123", - Name: "Alice Programmer", - Login: "alice@example.com", - }, - Services: map[string]string{ - "1xxaa": "First service", - "2baba": "Second service", - }, - Token: whoami.Token{ - ID: "abcdefg", - Name: "Token name", - CreatedAt: "2019-01-01T12:00:00Z", - // no ExpiresAt - Scope: "global", - }, -} - var basicOutput = "Alice Programmer \n" var basicOutputVerbose = strings.TrimSpace(` diff --git a/pkg/testutil/api.go b/pkg/testutil/api.go index 8c18712e1..753d80fea 100644 --- a/pkg/testutil/api.go +++ b/pkg/testutil/api.go @@ -1,9 +1,14 @@ package testutil import ( + "encoding/json" "errors" + "net/http" + "net/http/httptest" "github.com/fastly/go-fastly/v8/fastly" + + "github.com/fastly/cli/pkg/commands/whoami" ) // Err represents a generic error. @@ -61,3 +66,36 @@ func CloneVersionResult(version int) func(i *fastly.CloneVersionInput) (*fastly. func CloneVersionError(_ *fastly.CloneVersionInput) (*fastly.Version, error) { return nil, Err } + +// WhoamiVerifyClient is used by `whoami` and `sso` tests. +type WhoamiVerifyClient whoami.VerifyResponse + +func (c WhoamiVerifyClient) Do(*http.Request) (*http.Response, error) { + rec := httptest.NewRecorder() + _ = json.NewEncoder(rec).Encode(whoami.VerifyResponse(c)) + return rec.Result(), nil +} + +// WhoamiBasicResponse is used by `whoami` and `sso` tests. +var WhoamiBasicResponse = whoami.VerifyResponse{ + Customer: whoami.Customer{ + ID: "abc", + Name: "Computer Company", + }, + User: whoami.User{ + ID: "123", + Name: "Alice Programmer", + Login: "alice@example.com", + }, + Services: map[string]string{ + "1xxaa": "First service", + "2baba": "Second service", + }, + Token: whoami.Token{ + ID: "abcdefg", + Name: "Token name", + CreatedAt: "2019-01-01T12:00:00Z", + // no ExpiresAt + Scope: "global", + }, +} From c6a3c3f7f893c393ad3a2072269461b2ba2a74e8 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 10:37:41 +0100 Subject: [PATCH 069/115] refactor: move INFO output related to expired tokens to verbose mode --- pkg/app/run.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 3e1e2d6a3..3857d8b5f 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -304,7 +304,7 @@ func checkProfileToken( return tokenSource, warningMessage, nil } - if !g.Flags.Quiet { + if g.Flags.Verbose { text.Info(out, "Your access token has now expired. We will attempt to refresh it") } @@ -344,7 +344,7 @@ func checkProfileToken( refreshTokenCreated := current.RefreshTokenCreated refreshTokenTTL := current.RefreshTokenTTL if current.RefreshToken != updated.RefreshToken { - if !g.Flags.Quiet { + if g.Flags.Verbose { text.Info(out, "Your refresh token was also updated") text.Break(out) } From 467f4ee34d48a9c40019fb26138250b41ab0eb62 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 10:47:35 +0100 Subject: [PATCH 070/115] fix: set Authorization header alongside Fastly-Key --- pkg/api/undocumented/undocumented.go | 1 + pkg/commands/whoami/root.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index 3ecb8044b..f602608de 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -72,6 +72,7 @@ func Call(opts CallOptions) (data []byte, err error) { if opts.Token != "" { req.Header.Set("Fastly-Key", opts.Token) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", opts.Token)) } req.Header.Set("User-Agent", useragent.Name) for _, header := range opts.HTTPHeaders { diff --git a/pkg/commands/whoami/root.go b/pkg/commands/whoami/root.go index c29b7e876..229f366e4 100644 --- a/pkg/commands/whoami/root.go +++ b/pkg/commands/whoami/root.go @@ -38,8 +38,8 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { token, _ := c.Globals.Token() - // FIXME: Should we set `Authorization: Bearer `? req.Header.Set("Fastly-Key", token) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", useragent.Name) resp, err := c.Globals.HTTPClient.Do(req) From 1c644df413c66ce0df1805e6a7268e9bd21f710b Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 10:53:15 +0100 Subject: [PATCH 071/115] doc: warn callers of memory concern with undocumented.Call() --- pkg/api/undocumented/undocumented.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index f602608de..83b7037d6 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -61,6 +61,8 @@ type CallOptions struct { } // Call calls the given API endpoint and returns its response data. +// +// WARNING: Loads entire response body into memory. func Call(opts CallOptions) (data []byte, err error) { host := strings.TrimSuffix(opts.APIEndpoint, "/") endpoint := fmt.Sprintf("%s%s", host, opts.Path) From 2134a3286f893bcab1a5f76065adf22c68b586ad Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 10:59:16 +0100 Subject: [PATCH 072/115] refactor(whoami): replace manual request with existing abstraction --- pkg/commands/whoami/root.go | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pkg/commands/whoami/root.go b/pkg/commands/whoami/root.go index 229f366e4..584930f2d 100644 --- a/pkg/commands/whoami/root.go +++ b/pkg/commands/whoami/root.go @@ -6,8 +6,8 @@ import ( "io" "net/http" "sort" - "strings" + "github.com/fastly/cli/pkg/api/undocumented" "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/useragent" @@ -29,32 +29,32 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { - endpoint, _ := c.Globals.Endpoint() - fullurl := fmt.Sprintf("%s/verify", strings.TrimSuffix(endpoint, "/")) - req, err := http.NewRequest("GET", fullurl, nil) - if err != nil { - return fmt.Errorf("error constructing API request: %w", err) - } - token, _ := c.Globals.Token() - - req.Header.Set("Fastly-Key", token) - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) - req.Header.Set("Accept", "application/json") - req.Header.Set("User-Agent", useragent.Name) - resp, err := c.Globals.HTTPClient.Do(req) + apiEndpoint, _ := c.Globals.Endpoint() + data, err := undocumented.Call(undocumented.CallOptions{ + APIEndpoint: apiEndpoint, + HTTPClient: c.Globals.HTTPClient, + HTTPHeaders: []undocumented.HTTPHeader{ + { + Key: "Accept", + Value: "application/json", + }, + { + Key: "User-Agent", + Value: useragent.Name, + }, + }, + Method: http.MethodGet, + Path: "/verify", + Token: token, + }) if err != nil { c.Globals.ErrLog.Add(err) return fmt.Errorf("error executing API request: %w", err) } - defer resp.Body.Close() // #nosec G307 - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("error from API: %s", resp.Status) - } var response VerifyResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { + if err := json.Unmarshal(data, &response); err != nil { c.Globals.ErrLog.Add(err) return fmt.Errorf("error decoding API response: %w", err) } From 9ec48d0e37ff18bd68d0d6a90305a1721ea49d2f Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 11:17:21 +0100 Subject: [PATCH 073/115] feat: FASTLY_DEBUG_MODE --- cmd/fastly/main.go | 1 + pkg/app/run.go | 2 +- pkg/auth/auth.go | 9 +++++++-- pkg/commands/compute/deploy.go | 7 +++++-- pkg/commands/whoami/root.go | 3 +++ pkg/commands/whoami/whoami_test.go | 2 +- pkg/config/config.go | 5 ++++- pkg/env/env.go | 4 ++++ 8 files changed, 26 insertions(+), 7 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index bf8a36866..0fca4236f 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -90,6 +90,7 @@ func main() { result := make(chan auth.AuthorizationResult) router := http.NewServeMux() s := &auth.Server{ + DebugMode: e.DebugMode, HTTPClient: httpClient, Result: result, Router: router, diff --git a/pkg/app/run.go b/pkg/app/run.go index 3857d8b5f..ec8230662 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -324,7 +324,7 @@ func checkProfileToken( // Exchange the access token for a Fastly API token apiEndpoint, _ := g.Endpoint() - at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient) + at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient, g.Env.DebugMode) if err != nil { return tokenSource, warningMessage, fmt.Errorf("failed to exchange access token for an API token: %w", err) } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 1d5cf760a..1f6a5f8e3 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "strconv" "strings" "time" @@ -47,6 +48,8 @@ type Server struct { APIEndpoint string // AccountEndpoint is the accounts endpoint. AccountEndpoint string + // DebugMode indicates to the CLI it can display debug information. + DebugMode string // HTTPClient is a HTTP client used to call the API to exchange the access token for a session token. HTTPClient api.HTTPClient // Result is a channel that reports the result of authorization. @@ -170,7 +173,7 @@ func (s *Server) HandleCallback() http.HandlerFunc { } // Exchange the access token for a Fastly API token. - at, err := ExchangeAccessToken(j.AccessToken, s.APIEndpoint, s.HTTPClient) + at, err := ExchangeAccessToken(j.AccessToken, s.APIEndpoint, s.HTTPClient, s.DebugMode) if err != nil { s.Result <- AuthorizationResult{ Err: fmt.Errorf("failed to exchange access token for an API token: %w", err), @@ -364,7 +367,8 @@ func RefreshAccessToken(accountEndpoint, refreshToken string) (JWT, error) { } // ExchangeAccessToken exchanges `accessToken` for a Fastly API token. -func ExchangeAccessToken(accessToken, apiEndpoint string, httpClient api.HTTPClient) (*APIToken, error) { +func ExchangeAccessToken(accessToken, apiEndpoint string, httpClient api.HTTPClient, debugMode string) (*APIToken, error) { + debug, _ := strconv.ParseBool(debugMode) resp, err := undocumented.Call(undocumented.CallOptions{ APIEndpoint: apiEndpoint, HTTPClient: httpClient, @@ -376,6 +380,7 @@ func ExchangeAccessToken(accessToken, apiEndpoint string, httpClient api.HTTPCli }, Method: http.MethodPost, Path: "/login-enhanced", + Debug: debug, }) if err != nil { if apiErr, ok := err.(undocumented.APIError); ok { diff --git a/pkg/commands/compute/deploy.go b/pkg/commands/compute/deploy.go index fc8c76e65..92ccca77e 100644 --- a/pkg/commands/compute/deploy.go +++ b/pkg/commands/compute/deploy.go @@ -9,6 +9,7 @@ import ( "os" "os/signal" "path/filepath" + "strconv" "strings" "syscall" "time" @@ -379,7 +380,7 @@ func (c *DeployCommand) Setup(out io.Writer) (fnActivateTrial Activator, service } endpoint, _ := c.Globals.Endpoint() - fnActivateTrial = preconfigureActivateTrial(endpoint, token, c.Globals.HTTPClient) + fnActivateTrial = preconfigureActivateTrial(endpoint, token, c.Globals.HTTPClient, c.Globals.Env.DebugMode) return fnActivateTrial, serviceID, err } @@ -495,7 +496,8 @@ func packageSize(path string) (size int64, err error) { type Activator func(customerID string) error // preconfigureActivateTrial activates a free trial on the customer account. -func preconfigureActivateTrial(endpoint, token string, httpClient api.HTTPClient) Activator { +func preconfigureActivateTrial(endpoint, token string, httpClient api.HTTPClient, debugMode string) Activator { + debug, _ := strconv.ParseBool(debugMode) return func(customerID string) error { _, err := undocumented.Call(undocumented.CallOptions{ APIEndpoint: endpoint, @@ -503,6 +505,7 @@ func preconfigureActivateTrial(endpoint, token string, httpClient api.HTTPClient Method: http.MethodPost, Path: fmt.Sprintf(undocumented.EdgeComputeTrial, customerID), Token: token, + Debug: debug, }) if err != nil { apiErr, ok := err.(undocumented.APIError) diff --git a/pkg/commands/whoami/root.go b/pkg/commands/whoami/root.go index 584930f2d..95f06d1b5 100644 --- a/pkg/commands/whoami/root.go +++ b/pkg/commands/whoami/root.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "sort" + "strconv" "github.com/fastly/cli/pkg/api/undocumented" "github.com/fastly/cli/pkg/cmd" @@ -29,6 +30,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { + debugMode, _ := strconv.ParseBool(c.Globals.Env.DebugMode) token, _ := c.Globals.Token() apiEndpoint, _ := c.Globals.Endpoint() data, err := undocumented.Call(undocumented.CallOptions{ @@ -47,6 +49,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { Method: http.MethodGet, Path: "/verify", Token: token, + Debug: debugMode, }) if err != nil { c.Globals.ErrLog.Add(err) diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index 13a68c39b..9c2bf1e7f 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -42,7 +42,7 @@ func TestWhoami(t *testing.T) { name: "500 from API", args: args("whoami"), client: codeClient{code: http.StatusInternalServerError}, - wantError: "error from API: 500 Internal Server Error", + wantError: "error executing API request: error response", }, { name: "local error", diff --git a/pkg/config/config.go b/pkg/config/config.go index 31deee319..ded6323c0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -446,7 +446,9 @@ type Environment struct { Account string // APIToken is the env var we look in for the Fastly API token. APIToken string - // Endpoint is the env var we look in for the API endpoint. + // DebugMode indicates to the CLI it can display debug information. + DebugMode string + // Endpoint is the API endpoint to call. Endpoint string // UseSSO indicates if user wants to use SSO/OAuth token flow. // 1: enabled, 0: disabled. @@ -461,6 +463,7 @@ type Environment struct { func (e *Environment) Read(state map[string]string) { e.APIToken = state[env.APIToken] e.Account = state[env.Account] + e.DebugMode = state[env.DebugMode] e.Endpoint = state[env.Endpoint] e.Endpoint = state[env.Endpoint] e.UseSSO = state[env.UseSSO] diff --git a/pkg/env/env.go b/pkg/env/env.go index 5eb3060e0..b12398355 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -22,6 +22,10 @@ const ( // Assigned value should be a boolean 1/0 (enable/disable). UseSSO = "FASTLY_USE_SSO" + // DebugMode indicates to the CLI it can display debug information. + // Set to "true" to enable debug mode. + DebugMode = "FASTLY_DEBUG_MODE" + // Endpoint is the env var we look in for the API endpoint. Endpoint = "FASTLY_API_ENDPOINT" From f67911505cf55737a700cfa8d4b0a69665550756 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 11:37:31 +0100 Subject: [PATCH 074/115] refactor: remove unnecessary profile check --- pkg/app/run.go | 6 ---- pkg/profile/profile.go | 70 ------------------------------------------ 2 files changed, 76 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index ec8230662..d875d6e46 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -228,12 +228,6 @@ func processToken(commands []cmd.Command, commandName string, m *manifest.Data, checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, out) - // Check for a profile override. - token, err = profile.Init(token, m, g, in, out) - if err != nil { - return "", err - } - if g.Verbose() { displayToken(tokenSource, m.File.Profile, *g, out) } diff --git a/pkg/profile/profile.go b/pkg/profile/profile.go index e64bb5863..562d1b641 100644 --- a/pkg/profile/profile.go +++ b/pkg/profile/profile.go @@ -1,16 +1,7 @@ package profile import ( - "bytes" - "errors" - "fmt" - "io" - "github.com/fastly/cli/pkg/config" - fsterr "github.com/fastly/cli/pkg/errors" - "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/cli/pkg/text" ) // DefaultName is the default profile name. @@ -119,64 +110,3 @@ func Edit(name string, p config.Profiles, opts ...EditOption) (config.Profiles, } return p, ok } - -// Init checks if a profile flag is provided and potentially mutates the -// provided token. -// -// NOTE: If the specified profile doesn't exist, then we'll let the user decide -// if the default profile (if available) is acceptable to use instead. -func Init(token string, m *manifest.Data, g *global.Data, in io.Reader, out io.Writer) (string, error) { - // First check the fastly.toml manifest 'profile' field. - profile := m.File.Profile - - // Otherwise check the --profile global flag. - if profile == "" { - profile = g.Flags.Profile - } - - // If the user has not specified a profile override, either via the --profile - // flag or the manifest `profile` field, then we'll return back the token that - // was passed into this function. The token passed in was either provided by - // the --token flag or the FASTLY_API_TOKEN env var or potentially was found - // within the CLI's application configuration file (and if none of those, then - // the user would have authenticated via the interactive OAuth flow). - if profile == "" { - return token, nil - } - - p := Get(profile, g.Config.Profiles) - if p != nil { - return p.Token, nil - } - - msg := fmt.Sprintf(DoesNotExist, profile) - profile, p = Default(g.Config.Profiles) - if p == nil { - msg = fmt.Sprintf("%s (no account profiles configured)", msg) - return token, fsterr.RemediationError{ - Inner: fmt.Errorf(msg), - Remediation: fsterr.ProfileRemediation, - } - } - - // DoesNotExist is reused across errors and warning messages. Mostly errors - // and so when used here for a warning message, we need to uppercase the - // first letter so the warning reads like a proper sentence (where as golang - // errors should always be lowercase). - msg = fmt.Sprintf("%s%s. ", bytes.ToUpper([]byte(msg[:1])), msg[1:]) - msg = fmt.Sprintf("%sThe default profile '%s' (%s) will be used.", msg, profile, p.Email) - - if !g.Flags.AutoYes { - text.Warning(out, msg) - label := "\nWould you like to continue? [y/N] " - cont, err := text.AskYesNo(out, label, in) - if err != nil { - return token, err - } - if !cont { - return token, errors.New("command execution cancelled") - } - } - text.Break(out) - return p.Token, nil -} From 2623d240f27a472da3ec33bb4580f727c2743d23 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 11:45:55 +0100 Subject: [PATCH 075/115] fix(undocumented): print correct output --- pkg/api/undocumented/undocumented.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index 83b7037d6..940849315 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -102,7 +102,7 @@ func Call(opts CallOptions) (data []byte, err error) { if opts.Debug { dump, _ := httputil.DumpResponse(res, true) - fmt.Printf("undocumented.Call request dump:\n\n%#v\n\n", string(dump)) + fmt.Printf("undocumented.Call response dump:\n\n%#v\n\n", string(dump)) } data, err = io.ReadAll(res.Body) From a62326758407b0c314fcd60801142febdf7d4b04 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 12:10:03 +0100 Subject: [PATCH 076/115] refactor: deduplicate JWT validation and token logic --- pkg/app/run.go | 32 ++++++----------- pkg/auth/auth.go | 89 +++++++++++++++++++++++------------------------- 2 files changed, 53 insertions(+), 68 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index d875d6e46..4a6558b54 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -1,7 +1,6 @@ package app import ( - "errors" "fmt" "io" "os" @@ -301,26 +300,17 @@ func checkProfileToken( if g.Flags.Verbose { text.Info(out, "Your access token has now expired. We will attempt to refresh it") } + accountEndpoint, _ := g.Account() + apiEndpoint, _ := g.Endpoint() - account, _ := g.Account() - updated, err := auth.RefreshAccessToken(account, profileData.RefreshToken) + updatedJWT, err := auth.RefreshAccessToken(accountEndpoint, profileData.RefreshToken) if err != nil { return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) } - claims, err := auth.VerifyJWTSignature(account, updated.AccessToken) - if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to verify refreshed JWT: %w", err) - } - email, ok := claims["email"] - if !ok { - return tokenSource, warningMessage, errors.New("failed to extract email from JWT claims") - } - // Exchange the access token for a Fastly API token - apiEndpoint, _ := g.Endpoint() - at, err := auth.ExchangeAccessToken(updated.AccessToken, apiEndpoint, g.HTTPClient, g.Env.DebugMode) + email, at, err := auth.ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, updatedJWT.AccessToken, g.Env.DebugMode, g.HTTPClient) if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to exchange access token for an API token: %w", err) + return tokenSource, warningMessage, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) } // NOTE: The refresh token can sometimes be refreshed along with the access token. @@ -337,21 +327,21 @@ func checkProfileToken( refreshToken := current.RefreshToken refreshTokenCreated := current.RefreshTokenCreated refreshTokenTTL := current.RefreshTokenTTL - if current.RefreshToken != updated.RefreshToken { + if current.RefreshToken != updatedJWT.RefreshToken { if g.Flags.Verbose { text.Info(out, "Your refresh token was also updated") text.Break(out) } - refreshToken = updated.RefreshToken + refreshToken = updatedJWT.RefreshToken refreshTokenCreated = now - refreshTokenTTL = updated.RefreshExpiresIn + refreshTokenTTL = updatedJWT.RefreshExpiresIn } ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { - p.AccessToken = updated.AccessToken + p.AccessToken = updatedJWT.AccessToken p.AccessTokenCreated = now - p.AccessTokenTTL = updated.ExpiresIn - p.Email = email.(string) + p.AccessTokenTTL = updatedJWT.ExpiresIn + p.Email = email p.RefreshToken = refreshToken p.RefreshTokenCreated = refreshTokenCreated p.RefreshTokenTTL = refreshTokenTTL diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 1f6a5f8e3..95d51cded 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -123,7 +123,7 @@ func (s *Server) HandleCallback() http.HandlerFunc { return } - claims, err := VerifyJWTSignature(s.AccountEndpoint, j.AccessToken) + email, at, err := ValidateAndRetrieveAPIToken(s.AccountEndpoint, s.APIEndpoint, j.AccessToken, s.DebugMode, s.HTTPClient) if err != nil { s.Result <- AuthorizationResult{ Err: err, @@ -131,63 +131,58 @@ func (s *Server) HandleCallback() http.HandlerFunc { return } - azp, ok := claims["azp"] - if !ok { - s.Result <- AuthorizationResult{ - Err: errors.New("failed to extract azp from JWT claims"), - } - return - } - if azp != ClientID { - if !ok { - s.Result <- AuthorizationResult{ - Err: fmt.Errorf("failed to match expected azp: %s", azp), - } - return - } + fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") + s.Result <- AuthorizationResult{ + Email: email, + Jwt: j, + SessionToken: at.AccessToken, } + } +} + +// ValidateAndRetrieveAPIToken verifies the signature and the claims and +// exchanges the access token for an API token. +// +// NOTE: This function exists as it's called by this package + app.Run() +func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debugMode string, httpClient api.HTTPClient) (string, *APIToken, error) { + claims, err := VerifyJWTSignature(accountEndpoint, accessToken) + if err != nil { + return "", nil, err + } - aud, ok := claims["aud"] + azp, ok := claims["azp"] + if !ok { + return "", nil, errors.New("failed to extract azp from JWT claims") + } + if azp != ClientID { if !ok { - s.Result <- AuthorizationResult{ - Err: errors.New("failed to extract aud from JWT claims"), - } - return + return "", nil, fmt.Errorf("failed to match expected azp: %s", azp) } + } - if aud != s.APIEndpoint { - if !ok { - s.Result <- AuthorizationResult{ - Err: fmt.Errorf("failed to match expected aud: %s", s.APIEndpoint), - } - return - } - } + aud, ok := claims["aud"] + if !ok { + return "", nil, errors.New("failed to extract aud from JWT claims") + } - email, ok := claims["email"] + if aud != apiEndpoint { if !ok { - s.Result <- AuthorizationResult{ - Err: errors.New("failed to extract email from JWT claims"), - } - return + return "", nil, fmt.Errorf("failed to match expected aud: %s", apiEndpoint) } + } - // Exchange the access token for a Fastly API token. - at, err := ExchangeAccessToken(j.AccessToken, s.APIEndpoint, s.HTTPClient, s.DebugMode) - if err != nil { - s.Result <- AuthorizationResult{ - Err: fmt.Errorf("failed to exchange access token for an API token: %w", err), - } - return - } + email, ok := claims["email"] + if !ok { + return "", nil, errors.New("failed to extract email from JWT claims") + } - fmt.Fprint(w, "Authenticated successfully. Please close this page and return to the Fastly CLI in your terminal.") - s.Result <- AuthorizationResult{ - Email: email.(string), - Jwt: j, - SessionToken: at.AccessToken, - } + // Exchange the access token for a Fastly API token. + at, err := ExchangeAccessToken(accessToken, apiEndpoint, httpClient, debugMode) + if err != nil { + return "", nil, fmt.Errorf("failed to exchange access token for an API token: %w", err) } + + return email.(string), at, nil } // APIToken is returned from the /login-enhanced endpoint. From dd7774022840650566d220aeb82ea834247fbe76 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 15 Sep 2023 12:19:19 +0100 Subject: [PATCH 077/115] fix(profile/update): ensure SetDefault() is called --- pkg/commands/profile/update.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 5a4110d82..7d845d1d8 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -74,8 +74,10 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { if err != nil { return fmt.Errorf("failed to update token: %w", err) } - } else if makeDefault { // only set default if not updating the token (as updating the token already handles setting the default) - err := c.updateDefault(profileName) + } + + if makeDefault { + err := c.setAsDefault(profileName) if err != nil { return fmt.Errorf("failed to update token: %w", err) } @@ -168,7 +170,7 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con return nil } -func (c *UpdateCommand) updateDefault(profileName string) error { +func (c *UpdateCommand) setAsDefault(profileName string) error { p, ok := profile.SetDefault(profileName, c.Globals.Config.Profiles) if !ok { return errors.New("failed to update the profile's default field") From 71eb7489ea1910b05acb39a7dba99d0e611c2fe0 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 21 Sep 2023 17:38:24 +0100 Subject: [PATCH 078/115] fix: avoid breaking flow in profile commands --- pkg/commands/profile/create.go | 27 ++++++++++++--------------- pkg/commands/profile/update.go | 25 +++++++++---------------- pkg/errors/errors.go | 7 +++++++ 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index ace658d2a..59f3f3b82 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -29,6 +29,7 @@ type CreateCommand struct { automationToken bool clientFactory APIClientFactory profile string + sso bool } // NewCreateCommand returns a new command registered in the parent. @@ -39,12 +40,17 @@ func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data c.CmdClause = parent.Command("create", "Create user profile") c.CmdClause.Arg("profile", "Profile to create (default 'user')").Default(profile.DefaultName).Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) + c.CmdClause.Flag("sso", "Create an SSO-based token").BoolVar(&c.sso) c.clientFactory = cf return &c } // Exec implements the command interface. func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { + if c.sso && c.automationToken { + return fsterr.ErrInvalidProfileSSOCombo + } + if profile.Exist(c.profile, c.Globals.Config.Profiles) { return fsterr.RemediationError{ Inner: fmt.Errorf("profile '%s' already exists", c.profile), @@ -52,6 +58,11 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { } } + if !c.sso { + text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To create an SSO-based token, pass the `--sso` flag: `fastly profile create --sso`.") + text.Break(out) + } + // The Default status of a new profile should always be true unless there is // an existing profile already set to be the default. In the latter scenario // we should prompt the user to see if the new profile they're creating needs @@ -64,21 +75,7 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { } } - // Prompt user to decide on which token flow to take (OAuth or Static) - // Static being a traditional long-lived user/automation token. - // - // Opting for the OAuth flow will generate a short-lived token. - // Otherwise user has to create a token manually, then paste it when prompted. - useOAuthFlow := true - if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { - text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed.\n\n") - useOAuthFlow, err = text.AskYesNo(out, text.BoldYellow("Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: "), in) - text.Break(out) - if err != nil { - return err - } - } - if useOAuthFlow { + if c.sso { // IMPORTANT: We need to set profile fields for `sso` command. // // This is so the `sso` command will use this information to create diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index 7d845d1d8..b07f68eab 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -30,6 +30,7 @@ type UpdateCommand struct { automationToken bool clientFactory APIClientFactory profile string + sso bool } // NewUpdateCommand returns a usable command registered under the parent. @@ -40,6 +41,7 @@ func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data c.CmdClause = parent.Command("update", "Update user profile") c.CmdClause.Arg("profile", "Profile to update (defaults to the currently active profile)").Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) + c.CmdClause.Flag("sso", "Update profile to use an SSO-based token").BoolVar(&c.sso) c.clientFactory = cf return &c } @@ -123,24 +125,11 @@ func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { } func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *config.Profile, in io.Reader, out io.Writer) error { - // Prompt user to decide on which token flow to take (OAuth or Static) - // Static being a traditional long-lived user/automation token. - // - // Opting for the OAuth flow will generate a short-lived token. - // Otherwise user has to create a token manually, then paste it when prompted. - useOAuthFlow := true - if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { - text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to regenerate a short-lived token that can be automatically refreshed.") - text.Break(out) - var err error - useOAuthFlow, err = text.AskYesNo(out, text.BoldYellow("Continue with Fastly SSO (Single Sign-On) authentication for generating a short-lived token? [y/N]: "), in) - text.Break(out) - if err != nil { - return err - } + if !c.sso && !isSSOToken(p) { + text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To update this profile to use an SSO-based token, pass the `--sso` flag: `fastly profile update --sso`.") } - if useOAuthFlow { + if c.sso || isSSOToken(p) { // IMPORTANT: We need to set profile fields for `sso` command. // // This is so the `sso` command will use this information to update @@ -307,3 +296,7 @@ func (c *UpdateCommand) staticTokenFlow(profileName string, makeDefault bool, p return nil } + +func isSSOToken(p *config.Profile) bool { + return p.AccessToken != "" && p.RefreshToken != "" && p.AccessTokenCreated > 0 && p.RefreshTokenCreated > 0 +} diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index b6f7d7869..9419e28c6 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -155,6 +155,13 @@ var ErrInvalidStdinFileDirCombo = RemediationError{ Remediation: "Use only one of --stdin, --file or --dir.", } +// ErrInvalidProfileSSOCombo means the user specified both --sso and +// --automation-token and only one should be set. +var ErrInvalidProfileSSOCombo = RemediationError{ + Inner: fmt.Errorf("invalid command, both --sso and --automation-token provided"), + Remediation: "Provide at only one of: --sso or --automation-token, not both.", +} + // ErrInvalidEnableDisableFlagCombo means the user provided both a --enable // and --disable flag which are mutually exclusive behaviours. var ErrInvalidEnableDisableFlagCombo = RemediationError{ From a81423c8db3e5dc38fc75345317e674c9d795899 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 21 Sep 2023 18:17:15 +0100 Subject: [PATCH 079/115] fix(profile/update): put back the flow to how it was prior to sso --- pkg/commands/profile/update.go | 42 +++++++++------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index b07f68eab..e55c86fa6 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -54,28 +54,21 @@ func (c *UpdateCommand) Exec(in io.Reader, out io.Writer) error { } text.Info(out, "Profile being updated: '%s'.\n\n", profileName) + err = c.updateToken(profileName, p, in, out) + if err != nil { + return fmt.Errorf("failed to update token: %w", err) + } + // Set to true for --auto-yes/--non-interactive flags, otherwise prompt user. makeDefault := true - updateToken := true if !c.Globals.Flags.AutoYes && !c.Globals.Flags.NonInteractive { + text.Break(out) makeDefault, err = text.AskYesNo(out, text.BoldYellow("Make profile the default? [y/N] "), in) text.Break(out) if err != nil { return err } - - updateToken, err = text.AskYesNo(out, text.BoldYellow("Update the token associated with this profile? [y/N]: "), in) - if err != nil { - return err - } - } - - if updateToken { - err := c.updateToken(profileName, makeDefault, p, in, out) - if err != nil { - return fmt.Errorf("failed to update token: %w", err) - } } if makeDefault { @@ -124,7 +117,7 @@ func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { return profileName, p, nil } -func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *config.Profile, in io.Reader, out io.Writer) error { +func (c *UpdateCommand) updateToken(profileName string, p *config.Profile, in io.Reader, out io.Writer) error { if !c.sso && !isSSOToken(p) { text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To update this profile to use an SSO-based token, pass the `--sso` flag: `fastly profile update --sso`.") } @@ -136,7 +129,7 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con // the specific profile. c.authCmd.InvokedFromProfileUpdate = true c.authCmd.ProfileUpdateName = profileName - c.authCmd.ProfileDefault = makeDefault + c.authCmd.ProfileDefault = false // set to false, as later we prompt for this // NOTE: The `sso` command already handles writing config back to disk. // So unlike `c.staticTokenFlow` (below) we don't have to do that here. @@ -146,7 +139,7 @@ func (c *UpdateCommand) updateToken(profileName string, makeDefault bool, p *con } text.Break(out) } else { - if err := c.staticTokenFlow(profileName, makeDefault, p, in, out); err != nil { + if err := c.staticTokenFlow(profileName, p, in, out); err != nil { return fmt.Errorf("failed to process the static token flow: %w", err) } // Write the in-memory representation back to disk. @@ -226,7 +219,7 @@ func (c *UpdateCommand) validateToken(token, endpoint string, spinner text.Spinn return user.Login, nil } -func (c *UpdateCommand) staticTokenFlow(profileName string, makeDefault bool, p *config.Profile, in io.Reader, out io.Writer) error { +func (c *UpdateCommand) staticTokenFlow(profileName string, p *config.Profile, in io.Reader, out io.Writer) error { opts := []profile.EditOption{} token, err := text.InputSecure(out, text.BoldYellow("Profile token: (leave blank to skip): "), in) @@ -246,7 +239,7 @@ func (c *UpdateCommand) staticTokenFlow(profileName string, makeDefault bool, p text.Break(out) opts = append(opts, func(p *config.Profile) { - p.Default = makeDefault + p.Default = false // set to false, as later we prompt for this }) text.Break(out) @@ -281,19 +274,6 @@ func (c *UpdateCommand) staticTokenFlow(profileName string, makeDefault bool, p } c.Globals.Config.Profiles = ps - if makeDefault { - // We call SetDefault for its side effect of resetting all other profiles to have - // their Default field set to false. - ps, ok = profile.SetDefault(profileName, ps) - if !ok { - msg := fmt.Sprintf(profile.DoesNotExist, c.profile) - err := errors.New(msg) - c.Globals.ErrLog.Add(err) - return err - } - } - c.Globals.Config.Profiles = ps - return nil } From 49eea6e6fd5854c6dc018629655668a7c1e4daa0 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 22 Sep 2023 08:58:52 +0100 Subject: [PATCH 080/115] fix(testutil): correct annotations --- pkg/testutil/api.go | 1 + pkg/testutil/args.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/testutil/api.go b/pkg/testutil/api.go index 753d80fea..f2bd6d469 100644 --- a/pkg/testutil/api.go +++ b/pkg/testutil/api.go @@ -70,6 +70,7 @@ func CloneVersionError(_ *fastly.CloneVersionInput) (*fastly.Version, error) { // WhoamiVerifyClient is used by `whoami` and `sso` tests. type WhoamiVerifyClient whoami.VerifyResponse +// Do executes the HTTP request. func (c WhoamiVerifyClient) Do(*http.Request) (*http.Response, error) { rec := httptest.NewRecorder() _ = json.NewEncoder(rec).Encode(whoami.VerifyResponse(c)) diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index 4c0db2cdb..6c810fb0a 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -73,7 +73,7 @@ func (s MockAuthServer) SetAccountEndpoint(_ string) { // no-op } -// SetEndpoint sets the API endpoint. +// SetAPIEndpoint sets the API endpoint. func (s MockAuthServer) SetAPIEndpoint(_ string) { // no-op } From eeea0aca60318f7a9b57443b4cc8c30a983af8a5 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 7 Nov 2023 10:53:31 +0000 Subject: [PATCH 081/115] fix(undocumented): move response output to before error check --- pkg/api/undocumented/undocumented.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index 940849315..554ff4105 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -89,6 +89,12 @@ func Call(opts CallOptions) (data []byte, err error) { } res, err := opts.HTTPClient.Do(req) + + if opts.Debug && res != nil { + dump, _ := httputil.DumpResponse(res, true) + fmt.Printf("undocumented.Call response dump:\n\n%#v\n\n", string(dump)) + } + if err != nil { if urlErr, ok := err.(*url.Error); ok && urlErr.Timeout() { return data, fsterr.RemediationError{ @@ -100,11 +106,6 @@ func Call(opts CallOptions) (data []byte, err error) { } defer res.Body.Close() // #nosec G307 - if opts.Debug { - dump, _ := httputil.DumpResponse(res, true) - fmt.Printf("undocumented.Call response dump:\n\n%#v\n\n", string(dump)) - } - data, err = io.ReadAll(res.Body) if err != nil { return []byte{}, NewError(err, res.StatusCode) From 8c6e7317f60d5c80c50e1d465a882df1a83a3adb Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 7 Nov 2023 11:08:04 +0000 Subject: [PATCH 082/115] style(profile/update): add line breaks to info output --- pkg/commands/profile/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index e55c86fa6..a4a9f8dd2 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -119,7 +119,7 @@ func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { func (c *UpdateCommand) updateToken(profileName string, p *config.Profile, in io.Reader, out io.Writer) error { if !c.sso && !isSSOToken(p) { - text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To update this profile to use an SSO-based token, pass the `--sso` flag: `fastly profile update --sso`.") + text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To update this profile to use an SSO-based token, pass the `--sso` flag: `fastly profile update --sso`.\n\n") } if c.sso || isSSOToken(p) { From 42edcda7b724ae4a2e67e376a8a9fcd7058eb26a Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 7 Nov 2023 11:14:01 +0000 Subject: [PATCH 083/115] fix(config): remove duplicate key --- pkg/config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index ded6323c0..f1654660c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -465,7 +465,6 @@ func (e *Environment) Read(state map[string]string) { e.Account = state[env.Account] e.DebugMode = state[env.DebugMode] e.Endpoint = state[env.Endpoint] - e.Endpoint = state[env.Endpoint] e.UseSSO = state[env.UseSSO] e.WasmMetadataDisable = state[env.WasmMetadataDisable] } From 5291f7a8d8b91308f542c2451474bf51d3eb799c Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 7 Nov 2023 11:34:26 +0000 Subject: [PATCH 084/115] fix(app): move verbose flag out of signature --- pkg/app/run.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 4a6558b54..d0ae5de17 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -469,20 +469,18 @@ func checkConfigPermissions(quietMode bool, commandName string, tokenSource look } } -func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, verboseMode bool, out io.Writer) { - if verboseMode { - switch endpointSource { - case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) - case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) - case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) - case lookup.SourceDefault, lookup.SourceUndefined: - fallthrough - default: - fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) - } +func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Writer) { + switch endpointSource { + case lookup.SourceFlag: + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) + case lookup.SourceEnvironment: + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) + case lookup.SourceFile: + fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) + case lookup.SourceDefault, lookup.SourceUndefined: + fallthrough + default: + fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) } } From 1e2b2817a06ca2570ab6bb69cb70612bb490ca6d Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 7 Nov 2023 11:52:55 +0000 Subject: [PATCH 085/115] fix(app): add compute metadata to the no token switch --- pkg/app/run.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index d0ae5de17..ce69f1533 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -548,7 +548,9 @@ func commandCollectsData(command string) bool { // commandRequiresToken determines if the command to be executed is one that // requires an API token. func commandRequiresToken(command string) bool { - if command == "compute init" { + switch command { + case "compute init", "compute metadata": + // NOTE: Most `compute` commands require a token except init/metadata. return false } command = strings.Split(command, " ")[0] From fb0f286db75186849f8741ad9b0a9b3a496e88de Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 7 Nov 2023 12:00:09 +0000 Subject: [PATCH 086/115] fix(tests): remove extra line break --- pkg/app/run.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index ce69f1533..fedf346d1 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -472,15 +472,15 @@ func checkConfigPermissions(quietMode bool, commandName string, tokenSource look func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Writer) { switch endpointSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n", endpoint) case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.Endpoint, endpoint) case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n", endpoint) case lookup.SourceDefault, lookup.SourceUndefined: fallthrough default: - fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint: %s\n", endpoint) } } From b5a3303443d922e4bdc948691d2681c0b77e9dc8 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 9 Nov 2023 17:02:00 +0000 Subject: [PATCH 087/115] fix(undocumented): remove unnecessary Authorization header --- pkg/api/undocumented/undocumented.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/api/undocumented/undocumented.go b/pkg/api/undocumented/undocumented.go index 554ff4105..e224cd219 100644 --- a/pkg/api/undocumented/undocumented.go +++ b/pkg/api/undocumented/undocumented.go @@ -74,7 +74,6 @@ func Call(opts CallOptions) (data []byte, err error) { if opts.Token != "" { req.Header.Set("Fastly-Key", opts.Token) - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", opts.Token)) } req.Header.Set("User-Agent", useragent.Name) for _, header := range opts.HTTPHeaders { From e88be83a4559d8bed8de2a6327b1732dc6c8fe9c Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 9 Nov 2023 17:03:52 +0000 Subject: [PATCH 088/115] fix(config): bump config_version --- .fastly/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.fastly/config.toml b/.fastly/config.toml index a9648137c..f78d8bd7e 100644 --- a/.fastly/config.toml +++ b/.fastly/config.toml @@ -1,4 +1,4 @@ -config_version = 4 +config_version = 5 [fastly] account_endpoint = "https://accounts.fastly.com" From e91b9465052b9fd54295299563cc14d8960445ac Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 9 Nov 2023 18:03:39 +0000 Subject: [PATCH 089/115] refactor: rename Endpoint to APIEndpoint for clarity --- pkg/app/run.go | 14 +++++++------- pkg/commands/compute/deploy.go | 2 +- pkg/commands/logtail/root.go | 2 +- pkg/commands/profile/create.go | 2 +- pkg/commands/profile/update.go | 4 ++-- pkg/commands/sso/root.go | 2 +- pkg/commands/whoami/root.go | 2 +- pkg/global/global.go | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index fedf346d1..0426b42a0 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -91,9 +91,9 @@ func Run(opts RunOpts) error { opts.Manifest.File.SetQuiet(true) } - endpoint, endpointSource := g.Endpoint() + apiEndpoint, endpointSource := g.APIEndpoint() if g.Verbose() { - displayAPIEndpoint(endpoint, endpointSource, opts.Stdout) + displayAPIEndpoint(apiEndpoint, endpointSource, opts.Stdout) } token, err := processToken(commands, commandName, opts.Manifest, &g, opts.Stdin, opts.Stdout) @@ -101,7 +101,7 @@ func Run(opts RunOpts) error { return fmt.Errorf("failed to process token: %w", err) } - g.APIClient, g.RTSClient, err = configureClients(token, endpoint, opts.APIClient, g.Flags.Debug) + g.APIClient, g.RTSClient, err = configureClients(token, apiEndpoint, opts.APIClient, g.Flags.Debug) if err != nil { g.ErrLog.Add(err) return fmt.Errorf("error constructing client: %w", err) @@ -301,7 +301,7 @@ func checkProfileToken( text.Info(out, "Your access token has now expired. We will attempt to refresh it") } accountEndpoint, _ := g.Account() - apiEndpoint, _ := g.Endpoint() + apiEndpoint, _ := g.APIEndpoint() updatedJWT, err := auth.RefreshAccessToken(accountEndpoint, profileData.RefreshToken) if err != nil { @@ -484,8 +484,8 @@ func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Wr } } -func configureClients(token, endpoint string, acf APIClientFactory, debugMode bool) (apiClient api.Interface, rtsClient api.RealtimeStatsInterface, err error) { - apiClient, err = acf(token, endpoint, debugMode) +func configureClients(token, apiEndpoint string, acf APIClientFactory, debugMode bool) (apiClient api.Interface, rtsClient api.RealtimeStatsInterface, err error) { + apiClient, err = acf(token, apiEndpoint, debugMode) if err != nil { return nil, nil, fmt.Errorf("error constructing Fastly API client: %w", err) } @@ -512,7 +512,7 @@ func checkForUpdates(av github.AssetVersioner, commandName string, quietMode boo // the Run helper with it: in the real CLI, we can use NewClient from the Fastly // API client library via RealClient; in tests, we can provide a mock API // interface via MockClient. -type APIClientFactory func(token, endpoint string, debugMode bool) (api.Interface, error) +type APIClientFactory func(token, apiEndpoint string, debugMode bool) (api.Interface, error) // Versioners represents all supported versioner types. type Versioners struct { diff --git a/pkg/commands/compute/deploy.go b/pkg/commands/compute/deploy.go index 92ccca77e..2546e5ca9 100644 --- a/pkg/commands/compute/deploy.go +++ b/pkg/commands/compute/deploy.go @@ -379,7 +379,7 @@ func (c *DeployCommand) Setup(out io.Writer) (fnActivateTrial Activator, service return defaultActivator, serviceID, err } - endpoint, _ := c.Globals.Endpoint() + endpoint, _ := c.Globals.APIEndpoint() fnActivateTrial = preconfigureActivateTrial(endpoint, token, c.Globals.HTTPClient, c.Globals.Env.DebugMode) return fnActivateTrial, serviceID, err diff --git a/pkg/commands/logtail/root.go b/pkg/commands/logtail/root.go index 542559619..261300273 100644 --- a/pkg/commands/logtail/root.go +++ b/pkg/commands/logtail/root.go @@ -81,7 +81,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { c.Input.ServiceID = serviceID c.Input.Kind = fastly.ManagedLoggingInstanceOutput - endpoint, _ := c.Globals.Endpoint() + endpoint, _ := c.Globals.APIEndpoint() c.cfg.path = fmt.Sprintf("%s/service/%s/log_stream/managed/instance_output", endpoint, c.Input.ServiceID) c.dieCh = make(chan struct{}) diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 59f3f3b82..35a72baec 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -112,7 +112,7 @@ func (c *CreateCommand) staticTokenFlow(makeDefault bool, in io.Reader, out io.W } text.Break(out) - endpoint, _ := c.Globals.Endpoint() + endpoint, _ := c.Globals.APIEndpoint() spinner, err := text.NewSpinner(out) if err != nil { diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index a4a9f8dd2..e2de4d8b5 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -20,7 +20,7 @@ import ( // APIClientFactory allows the profile command to regenerate the global Fastly // API client when a new token is provided, in order to validate that token. // It's a redeclaration of the app.APIClientFactory to avoid an import loop. -type APIClientFactory func(token, endpoint string, debugMode bool) (api.Interface, error) +type APIClientFactory func(token, apiEndpoint string, debugMode bool) (api.Interface, error) // UpdateCommand represents a Kingpin command. type UpdateCommand struct { @@ -254,7 +254,7 @@ func (c *UpdateCommand) staticTokenFlow(profileName string, p *config.Profile, i } }() - endpoint, _ := c.Globals.Endpoint() + endpoint, _ := c.Globals.APIEndpoint() email, err := c.validateToken(token, endpoint, spinner) if err != nil { diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index a0e3f2698..ee9d7e099 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -71,7 +71,7 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { } accountEndpoint, _ := c.Globals.Account() - apiEndpoint, _ := c.Globals.Endpoint() + apiEndpoint, _ := c.Globals.APIEndpoint() verifier, err := auth.GenVerifier() if err != nil { return fsterr.RemediationError{ diff --git a/pkg/commands/whoami/root.go b/pkg/commands/whoami/root.go index 95f06d1b5..702111258 100644 --- a/pkg/commands/whoami/root.go +++ b/pkg/commands/whoami/root.go @@ -32,7 +32,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { debugMode, _ := strconv.ParseBool(c.Globals.Env.DebugMode) token, _ := c.Globals.Token() - apiEndpoint, _ := c.Globals.Endpoint() + apiEndpoint, _ := c.Globals.APIEndpoint() data, err := undocumented.Call(undocumented.CallOptions{ APIEndpoint: apiEndpoint, HTTPClient: c.Globals.HTTPClient, diff --git a/pkg/global/global.go b/pkg/global/global.go index be2e89e3d..0b24b62d9 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -113,8 +113,8 @@ func (d *Data) Verbose() bool { return d.Flags.Verbose } -// Endpoint yields the API endpoint. -func (d *Data) Endpoint() (string, lookup.Source) { +// APIEndpoint yields the API endpoint. +func (d *Data) APIEndpoint() (string, lookup.Source) { if d.Flags.Endpoint != "" { return d.Flags.Endpoint, lookup.SourceFlag } From 258ae61914a265bbf47fb06a34eb43f3d9941916 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 9 Nov 2023 18:18:05 +0000 Subject: [PATCH 090/115] style(app): add line breaks --- pkg/app/run.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 0426b42a0..2afcd7ccf 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -373,8 +373,7 @@ func shouldSkipSSO(profileName string, pd *config.Profile, out io.Writer, g *glo return false // don't skip OAuth } if !g.Flags.Quiet { - text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).", profileName) - text.Break(out) + text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) } return true // skip OAuth } @@ -472,15 +471,15 @@ func checkConfigPermissions(quietMode bool, commandName string, tokenSource look func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Writer) { switch endpointSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.Endpoint, endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) case lookup.SourceDefault, lookup.SourceUndefined: fallthrough default: - fmt.Fprintf(out, "Fastly API endpoint: %s\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) } } From 233b63b541c22def7f85afd003b11b4571039e25 Mon Sep 17 00:00:00 2001 From: Integralist Date: Thu, 9 Nov 2023 18:18:22 +0000 Subject: [PATCH 091/115] doc(auth): add well-known path --- pkg/auth/auth.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 95d51cded..aac1de48e 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -19,6 +19,9 @@ import ( fsterr "github.com/fastly/cli/pkg/errors" ) +// Well-Known configuration endpoint. +// https://accounts.fastly.com/realms/fastly/.well-known/openid-configuration + // Remediation is a generic remediation message for an error authorizing. const Remediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" From 26425c864ef39d53350a0cdd44642bef8c073806 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 11:15:38 +0000 Subject: [PATCH 092/115] style: tweak line breaks --- pkg/app/run.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 2afcd7ccf..448c7b8fd 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -225,12 +225,12 @@ func processToken(commands []cmd.Command, commandName string, m *manifest.Data, } } - checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, out) - if g.Verbose() { displayToken(tokenSource, m.File.Profile, *g, out) } + checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, out) + return token, nil } @@ -373,6 +373,9 @@ func shouldSkipSSO(profileName string, pd *config.Profile, out io.Writer, g *glo return false // don't skip OAuth } if !g.Flags.Quiet { + if g.Flags.Verbose { + text.Break(out) + } text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) } return true // skip OAuth @@ -471,15 +474,15 @@ func checkConfigPermissions(quietMode bool, commandName string, tokenSource look func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Writer) { switch endpointSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n", endpoint) case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n\n", env.Endpoint, endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.Endpoint, endpoint) case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n", endpoint) case lookup.SourceDefault, lookup.SourceUndefined: fallthrough default: - fmt.Fprintf(out, "Fastly API endpoint: %s\n\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint: %s\n", endpoint) } } From d2d677c2f2015f3a89318531a4b5d5adaecba5b9 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 11:16:43 +0000 Subject: [PATCH 093/115] fix(sso): hide command until GA --- pkg/commands/sso/root.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index ee9d7e099..b07b01828 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -43,7 +43,8 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) e c.authServer = authServer c.openBrowser = opener c.Globals = g - c.CmdClause = parent.Command("sso", "Single Sign-On authentication") + // FIXME: Unhide this command once SSO authentication goes GA. + c.CmdClause = parent.Command("sso", "Single Sign-On authentication").Hidden() c.CmdClause.Arg("profile", "Profile to authenticate (i.e. create/update a token for)").Short('p').StringVar(&c.profile) return &c } From ec87ab52ebb045757d3999704fe90e808b8acbc4 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 11:56:35 +0000 Subject: [PATCH 094/115] remove: SSO messaging until GA --- pkg/app/run.go | 38 ++++++++++++++++++---------------- pkg/app/run_test.go | 2 +- pkg/commands/profile/create.go | 9 ++++---- pkg/commands/profile/update.go | 7 ++++--- pkg/commands/sso/sso_test.go | 3 ++- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 448c7b8fd..a83d3e0e5 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -282,7 +282,7 @@ func checkProfileToken( // If OAuth flow has never been executed for the defined token, then we're // dealing with a user with a pre-existing traditional token and they've // opted into the OAuth flow. - if noOAuthToken(profileData) { + if noSSOToken(profileData) { warningMessage = "You've not authenticated via OAuth before" tokenSource = forceReAuth() return tokenSource, warningMessage, nil @@ -364,27 +364,29 @@ func checkProfileToken( return tokenSource, warningMessage, nil } -// shouldSkipSSO identifies if a config is a pre-v4 config and, if it is, -// informs the user how they can use the OAuth flow. It checks if the OAuth -// environment variable has been set and enables the OAuth flow if so. +// shouldSkipSSO identifies if a config is a pre-v5 config and, if it is, +// informs the user how they can use the SSO flow. It checks if the SSO +// environment variable has been set and enables the SSO flow if so. func shouldSkipSSO(profileName string, pd *config.Profile, out io.Writer, g *global.Data) bool { - if noOAuthToken(pd) { - if g.Env.UseSSO == "1" { - return false // don't skip OAuth - } - if !g.Flags.Quiet { - if g.Flags.Verbose { - text.Break(out) - } - text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) - } - return true // skip OAuth + if noSSOToken(pd) { + return g.Env.UseSSO != "1" + // FIXME: Put back messaging once SSO is GA. + // if g.Env.UseSSO == "1" { + // return false // don't skip SSO + // } + // if !g.Flags.Quiet { + // if g.Flags.Verbose { + // text.Break(out) + // } + // text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) + // } + // return true // skip SSO } - return false // don't skip OAuth + return false // don't skip SSO } -func noOAuthToken(pd *config.Profile) bool { - // If user has followed OAuth flow before, then these will not be zero values. +func noSSOToken(pd *config.Profile) bool { + // If user has followed SSO flow before, then these will not be zero values. return pd.AccessToken == "" && pd.RefreshToken == "" && pd.AccessTokenCreated == 0 && pd.RefreshTokenCreated == 0 } diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index 663c5b3e1..112f16151 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -53,11 +53,11 @@ _fastly_bash_autocomplete() { complete -F _fastly_bash_autocomplete fastly `, }, + // FIXME: Put back `sso` GA. { Name: "shell evaluate completion options", Args: args("--completion-bash"), WantOutput: `help -sso acl acl-entry auth-token diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 35a72baec..53d5cffb4 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -58,10 +58,11 @@ func (c *CreateCommand) Exec(in io.Reader, out io.Writer) (err error) { } } - if !c.sso { - text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To create an SSO-based token, pass the `--sso` flag: `fastly profile create --sso`.") - text.Break(out) - } + // FIXME: Put back messaging once SSO is GA. + // if !c.sso { + // text.Info(out, "When creating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To create an SSO-based token, pass the `--sso` flag: `fastly profile create --sso`.") + // text.Break(out) + // } // The Default status of a new profile should always be true unless there is // an existing profile already set to be the default. In the latter scenario diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index e2de4d8b5..b06d92232 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -118,9 +118,10 @@ func (c *UpdateCommand) identifyProfile() (string, *config.Profile, error) { } func (c *UpdateCommand) updateToken(profileName string, p *config.Profile, in io.Reader, out io.Writer) error { - if !c.sso && !isSSOToken(p) { - text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To update this profile to use an SSO-based token, pass the `--sso` flag: `fastly profile update --sso`.\n\n") - } + // FIXME: Put back messaging once SSO is GA. + // if !c.sso && !isSSOToken(p) { + // text.Info(out, "When updating a profile you can either paste in a long-lived token or allow the Fastly CLI to generate a short-lived token that can be automatically refreshed. To update this profile to use an SSO-based token, pass the `--sso` flag: `fastly profile update --sso`.\n\n") + // } if c.sso || isSSOToken(p) { // IMPORTANT: We need to set profile fields for `sso` command. diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index dbceb0054..364ca0351 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -139,7 +139,8 @@ func TestSSO(t *testing.T) { TestScenario: testutil.TestScenario{ Args: args("whoami"), WantOutputs: []string{ - "is not a Fastly SSO (Single Sign-On) generated token", + // FIXME: Put back messaging once SSO is GA. + // "is not a Fastly SSO (Single Sign-On) generated token", "Alice Programmer ", }, }, From 8c3de5d45f303b6bbbb5f60ad8172ac2c1eb6e5f Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 12:20:33 +0000 Subject: [PATCH 095/115] refactor(global): rename constants --- pkg/global/global.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/global/global.go b/pkg/global/global.go index 0b24b62d9..7ebc78cef 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -10,11 +10,11 @@ import ( "github.com/fastly/cli/pkg/manifest" ) -// DefaultEndpoint is the default Fastly API endpoint. -const DefaultEndpoint = "https://api.fastly.com" +// DefaultAPIEndpoint is the default Fastly API endpoint. +const DefaultAPIEndpoint = "https://api.fastly.com" -// DefaultAccount is the default Fastly Accounts endpoint. -const DefaultAccount = "https://accounts.fastly.com" +// DefaultAccountEndpoint is the default Fastly Accounts endpoint. +const DefaultAccountEndpoint = "https://accounts.fastly.com" // Data holds global-ish configuration data from all sources: environment // variables, config files, and flags. It has methods to give each parameter to @@ -123,11 +123,11 @@ func (d *Data) APIEndpoint() (string, lookup.Source) { return d.Env.Endpoint, lookup.SourceEnvironment } - if d.Config.Fastly.APIEndpoint != DefaultEndpoint && d.Config.Fastly.APIEndpoint != "" { + if d.Config.Fastly.APIEndpoint != DefaultAPIEndpoint && d.Config.Fastly.APIEndpoint != "" { return d.Config.Fastly.APIEndpoint, lookup.SourceFile } - return DefaultEndpoint, lookup.SourceDefault // this method should not fail + return DefaultAPIEndpoint, lookup.SourceDefault // this method should not fail } // Account yields the Accounts endpoint. @@ -140,11 +140,11 @@ func (d *Data) Account() (string, lookup.Source) { return d.Env.Account, lookup.SourceEnvironment } - if d.Config.Fastly.AccountEndpoint != DefaultAccount && d.Config.Fastly.AccountEndpoint != "" { + if d.Config.Fastly.AccountEndpoint != DefaultAccountEndpoint && d.Config.Fastly.AccountEndpoint != "" { return d.Config.Fastly.AccountEndpoint, lookup.SourceFile } - return DefaultAccount, lookup.SourceDefault // this method should not fail + return DefaultAccountEndpoint, lookup.SourceDefault // this method should not fail } // Flags represents all of the configuration parameters that can be set with From 2f72741c506c386aecb6bf57cc1c7b6647c7eee0 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 12:34:25 +0000 Subject: [PATCH 096/115] refactor(profile): hide sso flag until GA --- pkg/commands/profile/create.go | 2 +- pkg/commands/profile/update.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index 53d5cffb4..eea8b8110 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -40,7 +40,7 @@ func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data c.CmdClause = parent.Command("create", "Create user profile") c.CmdClause.Arg("profile", "Profile to create (default 'user')").Default(profile.DefaultName).Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) - c.CmdClause.Flag("sso", "Create an SSO-based token").BoolVar(&c.sso) + c.CmdClause.Flag("sso", "Create an SSO-based token").Hidden().BoolVar(&c.sso) c.clientFactory = cf return &c } diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index b06d92232..b5d17e472 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -41,7 +41,7 @@ func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data c.CmdClause = parent.Command("update", "Update user profile") c.CmdClause.Arg("profile", "Profile to update (defaults to the currently active profile)").Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) - c.CmdClause.Flag("sso", "Update profile to use an SSO-based token").BoolVar(&c.sso) + c.CmdClause.Flag("sso", "Update profile to use an SSO-based token").Hidden().BoolVar(&c.sso) c.clientFactory = cf return &c } From 59bd239583c3fc0fbedf3c52daca20c1dde19212 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 13:41:35 +0000 Subject: [PATCH 097/115] fix(auth): check type assert --- pkg/auth/auth.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index aac1de48e..96ac0ec9c 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -146,7 +146,7 @@ func (s *Server) HandleCallback() http.HandlerFunc { // ValidateAndRetrieveAPIToken verifies the signature and the claims and // exchanges the access token for an API token. // -// NOTE: This function exists as it's called by this package + app.Run() +// NOTE: This function exists as it's called by this package + app.Run(). func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debugMode string, httpClient api.HTTPClient) (string, *APIToken, error) { claims, err := VerifyJWTSignature(accountEndpoint, accessToken) if err != nil { @@ -185,7 +185,11 @@ func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debu return "", nil, fmt.Errorf("failed to exchange access token for an API token: %w", err) } - return email.(string), at, nil + e, ok := email.(string) + if !ok { + return "", nil, fmt.Errorf("failed to type assert 'email' (%#v) to a string", email) + } + return e, at, nil } // APIToken is returned from the /login-enhanced endpoint. From 59ff6ac8f4f8e7d2b70b3d67e27421dd5ec9d402 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 13:51:17 +0000 Subject: [PATCH 098/115] refactor(main): rename s to authServer --- cmd/fastly/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index 0fca4236f..d7d4eb9e9 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -89,13 +89,13 @@ func main() { // We do this here so that we can mock the values in our test suite. result := make(chan auth.AuthorizationResult) router := http.NewServeMux() - s := &auth.Server{ + authServer := &auth.Server{ DebugMode: e.DebugMode, HTTPClient: httpClient, Result: result, Router: router, } - router.HandleFunc("/callback", s.HandleCallback()) + router.HandleFunc("/callback", authServer.HandleCallback()) // The `main` function is a shim for calling `app.Run()`. err = app.Run(app.RunOpts{ @@ -107,7 +107,7 @@ func main() { return client, err }, Args: args, - AuthServer: s, + AuthServer: authServer, ConfigFile: cfg, ConfigPath: config.FilePath, Env: e, From 4c50a347aa9e5a1213af59414365345c31da7720 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 14:14:27 +0000 Subject: [PATCH 099/115] feat: store .well-known inside of auth.Server --- cmd/fastly/main.go | 49 ++++++++++++++++++++++++++++++++++------------ pkg/auth/auth.go | 7 +++++-- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index d7d4eb9e9..ad7892c48 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -3,6 +3,7 @@ package main import ( "errors" + "fmt" "io" "net/http" "os" @@ -87,13 +88,33 @@ func main() { // Configure authentication inputs. // We do this here so that we can mock the values in our test suite. + req, err := http.NewRequest(http.MethodGet, auth.WellKnown, nil) + if err != nil { + err = fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) + handleErr(err, out) + os.Exit(1) + } + resp, err := httpClient.Do(req) + if err != nil { + err = fmt.Errorf("failed to request OpenID Connect .well-known metadata: %w", err) + handleErr(err, out) + os.Exit(1) + } + openIDConfig, err := io.ReadAll(resp.Body) + if err != nil { + err = fmt.Errorf("failed to read OpenID Connect .well-known metadata: %w", err) + handleErr(err, out) + os.Exit(1) + } + _ = resp.Body.Close() result := make(chan auth.AuthorizationResult) router := http.NewServeMux() authServer := &auth.Server{ - DebugMode: e.DebugMode, - HTTPClient: httpClient, - Result: result, - Router: router, + DebugMode: e.DebugMode, + HTTPClient: httpClient, + OpenIDConfig: openIDConfig, + Result: result, + Router: router, } router.HandleFunc("/callback", authServer.HandleCallback()) @@ -153,14 +174,18 @@ func main() { fsterr.Deduce(logErr).Print(color.Error) } if err != nil { - text.Break(out) - fsterr.Deduce(err).Print(color.Error) - exitError := fsterr.SkipExitError{} - if errors.As(err, &exitError) { - if exitError.Skip { - return // skip returning an error for 'help' output - } - } + handleErr(err, out) os.Exit(1) } } + +func handleErr(err error, out io.Writer) { + text.Break(out) + fsterr.Deduce(err).Print(color.Error) + exitError := fsterr.SkipExitError{} + if errors.As(err, &exitError) { + if exitError.Skip { + return // skip returning an error for 'help' output + } + } +} diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 96ac0ec9c..0849654f0 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -19,8 +19,9 @@ import ( fsterr "github.com/fastly/cli/pkg/errors" ) -// Well-Known configuration endpoint. -// https://accounts.fastly.com/realms/fastly/.well-known/openid-configuration +// WellKnown is OpenID Connect's metadata discovery mechanism. +// https://swagger.io/docs/specification/authentication/openid-connect-discovery/ +const WellKnown = "https://accounts.fastly.com/realms/fastly/.well-known/openid-configuration" // Remediation is a generic remediation message for an error authorizing. const Remediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" @@ -55,6 +56,8 @@ type Server struct { DebugMode string // HTTPClient is a HTTP client used to call the API to exchange the access token for a session token. HTTPClient api.HTTPClient + // OpenIDConfig is the .well-known metadata. + OpenIDConfig []byte // Result is a channel that reports the result of authorization. Result chan AuthorizationResult // Router is an HTTP request multiplexer. From cfb844933deb2f0d5bded8d0fa3e5b335dd1a04a Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 14:29:39 +0000 Subject: [PATCH 100/115] refactor: store well-known as struct not bytes --- cmd/fastly/main.go | 20 ++++++++++++++------ pkg/auth/auth.go | 22 ++++++++++++++++------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index ad7892c48..6fa1d701a 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -2,6 +2,7 @@ package main import ( + "encoding/json" "errors" "fmt" "io" @@ -88,7 +89,7 @@ func main() { // Configure authentication inputs. // We do this here so that we can mock the values in our test suite. - req, err := http.NewRequest(http.MethodGet, auth.WellKnown, nil) + req, err := http.NewRequest(http.MethodGet, auth.OIDCMetadata, nil) if err != nil { err = fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) handleErr(err, out) @@ -107,14 +108,21 @@ func main() { os.Exit(1) } _ = resp.Body.Close() + var wellknown auth.WellKnownEndpoints + err = json.Unmarshal(openIDConfig, &wellknown) + if err != nil { + err = fmt.Errorf("failed to unmarshal OpenID Connect .well-known metadata: %w", err) + handleErr(err, out) + os.Exit(1) + } result := make(chan auth.AuthorizationResult) router := http.NewServeMux() authServer := &auth.Server{ - DebugMode: e.DebugMode, - HTTPClient: httpClient, - OpenIDConfig: openIDConfig, - Result: result, - Router: router, + DebugMode: e.DebugMode, + HTTPClient: httpClient, + Result: result, + Router: router, + WellKnownEndpoints: wellknown, } router.HandleFunc("/callback", authServer.HandleCallback()) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 0849654f0..1ca2a0f04 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -19,10 +19,6 @@ import ( fsterr "github.com/fastly/cli/pkg/errors" ) -// WellKnown is OpenID Connect's metadata discovery mechanism. -// https://swagger.io/docs/specification/authentication/openid-connect-discovery/ -const WellKnown = "https://accounts.fastly.com/realms/fastly/.well-known/openid-configuration" - // Remediation is a generic remediation message for an error authorizing. const Remediation = "Please re-run the command. If the problem persists, please file an issue: https://github.com/fastly/cli/issues/new?labels=bug&template=bug_report.md" @@ -32,6 +28,20 @@ const ClientID = "fastly-cli" // RedirectURL is the endpoint the auth provider will pass an authorization code to. const RedirectURL = "http://localhost:8080/callback" +// OIDCMetadata is OpenID Connect's metadata discovery mechanism. +// https://swagger.io/docs/specification/authentication/openid-connect-discovery/ +const OIDCMetadata = "https://accounts.fastly.com/realms/fastly/.well-known/openid-configuration" + +// WellKnownEndpoints represents the OpenID Connect metadata. +type WellKnownEndpoints struct { + // Auth is the authorization_endpoint. + Auth string `json:"authorization_endpoint"` + // Certs is the jwks_uri. + Certs string `json:"jwks_uri"` + // Token is the token_endpoint. + Token string `json:"token_endpoint"` +} + // Starter defines the behaviour for the authentication server. type Starter interface { // GetResult returns the results channel @@ -56,14 +66,14 @@ type Server struct { DebugMode string // HTTPClient is a HTTP client used to call the API to exchange the access token for a session token. HTTPClient api.HTTPClient - // OpenIDConfig is the .well-known metadata. - OpenIDConfig []byte // Result is a channel that reports the result of authorization. Result chan AuthorizationResult // Router is an HTTP request multiplexer. Router *http.ServeMux // Verifier represents an OAuth PKCE code verifier that uses the S256 challenge method. Verifier *oidc.S256Verifier + // WellKnownEndpoints is the .well-known metadata. + WellKnownEndpoints WellKnownEndpoints } // GetResult returns the result channel. From d0552f79a566e6b00696a749aeeab87723491051 Mon Sep 17 00:00:00 2001 From: Integralist Date: Fri, 10 Nov 2023 14:37:48 +0000 Subject: [PATCH 101/115] refactor: rename Account to AccountEndpoint --- pkg/app/run.go | 2 +- pkg/commands/sso/root.go | 2 +- pkg/global/global.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index a83d3e0e5..6f43916c9 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -300,7 +300,7 @@ func checkProfileToken( if g.Flags.Verbose { text.Info(out, "Your access token has now expired. We will attempt to refresh it") } - accountEndpoint, _ := g.Account() + accountEndpoint, _ := g.AccountEndpoint() apiEndpoint, _ := g.APIEndpoint() updatedJWT, err := auth.RefreshAccessToken(accountEndpoint, profileData.RefreshToken) diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index b07b01828..ba133b762 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -71,7 +71,7 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { } } - accountEndpoint, _ := c.Globals.Account() + accountEndpoint, _ := c.Globals.AccountEndpoint() apiEndpoint, _ := c.Globals.APIEndpoint() verifier, err := auth.GenVerifier() if err != nil { diff --git a/pkg/global/global.go b/pkg/global/global.go index 7ebc78cef..889202c3f 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -130,8 +130,8 @@ func (d *Data) APIEndpoint() (string, lookup.Source) { return DefaultAPIEndpoint, lookup.SourceDefault // this method should not fail } -// Account yields the Accounts endpoint. -func (d *Data) Account() (string, lookup.Source) { +// AccountEndpoint yields the Accounts endpoint. +func (d *Data) AccountEndpoint() (string, lookup.Source) { if d.Flags.Account != "" { return d.Flags.Account, lookup.SourceFlag } From 45a5e550397b2efbcf039e281490f26b04f0ce7a Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 13 Nov 2023 10:45:04 +0000 Subject: [PATCH 102/115] refactor: move main logic to custom init --- cmd/fastly/main.go | 174 +----------------- pkg/app/commands.go | 20 +- pkg/app/run.go | 172 ++++++++++++++++- pkg/app/run_test.go | 7 +- pkg/app/usage.go | 51 ++--- pkg/commands/acl/acl_test.go | 46 +++-- pkg/commands/aclentry/aclentry_test.go | 46 +++-- pkg/commands/authtoken/authtoken_test.go | 37 ++-- pkg/commands/backend/backend_test.go | 46 +++-- pkg/commands/compute/build_test.go | 137 +++++++------- pkg/commands/compute/deploy_test.go | 12 +- pkg/commands/compute/init_test.go | 19 +- pkg/commands/compute/metadata_test.go | 6 +- pkg/commands/compute/pack_test.go | 7 +- pkg/commands/compute/update_test.go | 10 +- pkg/commands/compute/validate_test.go | 7 +- pkg/commands/config/config_test.go | 12 +- pkg/commands/configstore/configstore_test.go | 73 ++++---- .../configstoreentry/configstoreentry_test.go | 62 +++---- pkg/commands/dictionary/dictionary_test.go | 46 +++-- .../dictionaryentry/dictionaryitem_test.go | 46 +++-- pkg/commands/domain/domain_test.go | 55 ++++-- pkg/commands/healthcheck/healthcheck_test.go | 46 +++-- pkg/commands/ip/ip_test.go | 10 +- pkg/commands/kvstore/kvstore_test.go | 49 ++--- .../kvstoreentry/kvstoreentry_test.go | 52 +++--- .../azureblob/azureblob_integration_test.go | 46 +++-- .../bigquery/bigquery_integration_test.go | 46 +++-- .../cloudfiles/cloudfiles_integration_test.go | 46 +++-- .../datadog/datadog_integration_test.go | 46 +++-- .../digitalocean_integration_test.go | 46 +++-- .../elasticsearch_integration_test.go | 46 +++-- .../logging/ftp/ftp_integration_test.go | 46 +++-- .../logging/gcs/gcs_integration_test.go | 46 +++-- .../googlepubsub_integration_test.go | 46 +++-- .../logging/heroku/heroku_integration_test.go | 46 +++-- .../honeycomb/honeycomb_integration_test.go | 46 +++-- .../logging/https/https_integration_test.go | 46 +++-- .../logging/kafka/kafka_integration_test.go | 46 +++-- .../kinesis/kinesis_integration_test.go | 58 +++--- .../logging/loggly/loggly_integration_test.go | 46 +++-- .../logshuttle/logshuttle_integration_test.go | 46 +++-- .../logging/newrelic/newrelic_test.go | 46 +++-- .../logging/newrelicotlp/newrelicotlp_test.go | 46 +++-- .../openstack/openstack_integration_test.go | 46 +++-- .../papertrail/papertrail_integration_test.go | 46 +++-- .../logging/s3/s3_integration_test.go | 46 +++-- .../logging/scalyr/scalyr_integration_test.go | 46 +++-- .../logging/sftp/sftp_integration_test.go | 46 +++-- .../logging/splunk/splunk_integration_test.go | 58 +++--- .../sumologic/sumologic_integration_test.go | 46 +++-- .../logging/syslog/syslog_integration_test.go | 46 +++-- pkg/commands/pop/pop_test.go | 10 +- pkg/commands/products/products_test.go | 10 +- pkg/commands/profile/profile_test.go | 52 ++++-- pkg/commands/purge/purge_test.go | 37 ++-- pkg/commands/ratelimit/ratelimit_test.go | 46 +++-- .../resourcelink/resourcelink_test.go | 59 +++--- pkg/commands/secretstore/secretstore_test.go | 45 +++-- .../secretstoreentry/secretstoreentry_test.go | 45 +++-- pkg/commands/service/service_test.go | 55 ++++-- pkg/commands/serviceauth/service_test.go | 46 +++-- .../serviceversion/serviceversion_test.go | 55 ++++-- pkg/commands/sso/sso_test.go | 21 ++- pkg/commands/stats/historical_test.go | 10 +- pkg/commands/stats/regions_test.go | 13 +- pkg/commands/tls/config/config_test.go | 31 +++- .../tls/custom/activation/activation_test.go | 49 +++-- .../custom/certificate/certificate_test.go | 46 +++-- pkg/commands/tls/custom/domain/domain_test.go | 13 +- .../tls/custom/privatekey/privatekey_test.go | 40 ++-- pkg/commands/tls/platform/platform_test.go | 49 +++-- .../tls/subscription/subscription_test.go | 49 +++-- pkg/commands/user/user_test.go | 46 +++-- pkg/commands/vcl/condition/condition_test.go | 46 +++-- pkg/commands/vcl/custom/custom_test.go | 46 +++-- pkg/commands/vcl/snippet/snippet_test.go | 46 +++-- pkg/commands/version/version_test.go | 6 +- pkg/commands/whoami/whoami_test.go | 6 +- pkg/testutil/args.go | 6 +- 80 files changed, 2190 insertions(+), 1261 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index 6fa1d701a..19c224177 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -2,198 +2,42 @@ package main import ( - "encoding/json" "errors" - "fmt" "io" - "net/http" "os" - "time" - "github.com/fastly/go-fastly/v8/fastly" "github.com/fatih/color" - "github.com/skratchdot/open-golang/open" - "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/app" - "github.com/fastly/cli/pkg/auth" - "github.com/fastly/cli/pkg/commands/compute" - "github.com/fastly/cli/pkg/config" - "github.com/fastly/cli/pkg/env" fsterr "github.com/fastly/cli/pkg/errors" - "github.com/fastly/cli/pkg/github" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/cli/pkg/sync" "github.com/fastly/cli/pkg/text" ) func main() { - // Parse the arguments provided by the user via the command-line interface. - args := os.Args[1:] - - // Define a HTTP client that will be used for making arbitrary HTTP requests. - httpClient := &http.Client{Timeout: time.Minute * 2} - - // Define the standard input/output streams. - var ( - in io.Reader = os.Stdin - out io.Writer = sync.NewWriter(color.Output) - ) - - // Read relevant configuration options from the user's environment. - var e config.Environment - e.Read(env.Parse(os.Environ())) - - // Identify verbose flag early (before Kingpin parser has executed) so we can - // print additional output related to the CLI configuration. - var verboseOutput bool - for _, seg := range args { - if seg == "-v" || seg == "--verbose" { - verboseOutput = true - } - } - - // Identify auto-yes/non-interactive flag early (before Kingpin parser has - // executed) so we can handle the interactive prompts appropriately with - // regards to processing the CLI configuration. - var autoYes, nonInteractive bool - for _, seg := range args { - if seg == "-y" || seg == "--auto-yes" { - autoYes = true - } - if seg == "-i" || seg == "--non-interactive" { - nonInteractive = true + if err := app.Run(os.Args, os.Stdin); err != nil { + if skipExit := processErr(err, os.Args, os.Stdout); skipExit { + return } - } - - // Extract a subset of configuration options from the local app directory. - var cfg config.File - cfg.SetAutoYes(autoYes) - cfg.SetNonInteractive(nonInteractive) - // The CLI relies on a valid configuration, otherwise we can't continue. - err := cfg.Read(config.FilePath, in, out, fsterr.Log, verboseOutput) - if err != nil { - fsterr.Deduce(err).Print(color.Error) - // WARNING: os.Exit will exit, and any `defer` calls will not be run. - os.Exit(1) - } - - // Extract user's project configuration from the fastly.toml manifest. - var md manifest.Data - md.File.Args = args - md.File.SetErrLog(fsterr.Log) - md.File.SetOutput(out) - - // NOTE: We skip handling the error because not all commands relate to Compute. - _ = md.File.Read(manifest.Filename) - - // Configure authentication inputs. - // We do this here so that we can mock the values in our test suite. - req, err := http.NewRequest(http.MethodGet, auth.OIDCMetadata, nil) - if err != nil { - err = fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) - handleErr(err, out) os.Exit(1) } - resp, err := httpClient.Do(req) - if err != nil { - err = fmt.Errorf("failed to request OpenID Connect .well-known metadata: %w", err) - handleErr(err, out) - os.Exit(1) - } - openIDConfig, err := io.ReadAll(resp.Body) - if err != nil { - err = fmt.Errorf("failed to read OpenID Connect .well-known metadata: %w", err) - handleErr(err, out) - os.Exit(1) - } - _ = resp.Body.Close() - var wellknown auth.WellKnownEndpoints - err = json.Unmarshal(openIDConfig, &wellknown) - if err != nil { - err = fmt.Errorf("failed to unmarshal OpenID Connect .well-known metadata: %w", err) - handleErr(err, out) - os.Exit(1) - } - result := make(chan auth.AuthorizationResult) - router := http.NewServeMux() - authServer := &auth.Server{ - DebugMode: e.DebugMode, - HTTPClient: httpClient, - Result: result, - Router: router, - WellKnownEndpoints: wellknown, - } - router.HandleFunc("/callback", authServer.HandleCallback()) - - // The `main` function is a shim for calling `app.Run()`. - err = app.Run(app.RunOpts{ - APIClient: func(token, endpoint string, debugMode bool) (api.Interface, error) { - client, err := fastly.NewClientForEndpoint(token, endpoint) - if debugMode { - client.DebugMode = true - } - return client, err - }, - Args: args, - AuthServer: authServer, - ConfigFile: cfg, - ConfigPath: config.FilePath, - Env: e, - ErrLog: fsterr.Log, - ExecuteWasmTools: compute.ExecuteWasmTools, - HTTPClient: httpClient, - Manifest: &md, - Opener: open.Run, - Stdin: in, - Stdout: out, - Versioners: app.Versioners{ - CLI: github.New(github.Opts{ - HTTPClient: httpClient, - Org: "fastly", - Repo: "cli", - Binary: "fastly", - }), - Viceroy: github.New(github.Opts{ - HTTPClient: httpClient, - Org: "fastly", - Repo: "viceroy", - Binary: "viceroy", - Version: md.File.LocalServer.ViceroyVersion, - }), - WasmTools: github.New(github.Opts{ - HTTPClient: httpClient, - Org: "bytecodealliance", - Repo: "wasm-tools", - Binary: "wasm-tools", - External: true, - Nested: true, - }), - }, - }) +} +// processErr persists the error log to disk and deduces the error type. +func processErr(err error, args []string, out io.Writer) (skipExit bool) { // NOTE: We persist any error log entries to disk before attempting to handle // a possible error response from app.Run as there could be errors recorded // during the execution flow but were otherwise handled without bubbling an // error back the call stack, and so if the user still experiences something // unexpected we will have a record of any errors that happened along the way. - logErr := fsterr.Log.Persist(fsterr.LogPath, args) + logErr := fsterr.Log.Persist(fsterr.LogPath, args[1:]) if logErr != nil { fsterr.Deduce(logErr).Print(color.Error) } - if err != nil { - handleErr(err, out) - os.Exit(1) - } -} - -func handleErr(err error, out io.Writer) { text.Break(out) fsterr.Deduce(err).Print(color.Error) exitError := fsterr.SkipExitError{} if errors.As(err, &exitError) { - if exitError.Skip { - return // skip returning an error for 'help' output - } + return exitError.Skip } + return false } diff --git a/pkg/app/commands.go b/pkg/app/commands.go index 58a04eb81..7b8657338 100644 --- a/pkg/app/commands.go +++ b/pkg/app/commands.go @@ -3,6 +3,7 @@ package app import ( "github.com/fastly/kingpin" + "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/acl" "github.com/fastly/cli/pkg/commands/aclentry" @@ -85,7 +86,10 @@ func defineCommands( app *kingpin.Application, g *global.Data, m manifest.Data, - opts RunOpts, + opener func(string) error, + authServer auth.Starter, + versioners Versioners, + factory APIClientFactory, ) []cmd.Command { shellcompleteCmdRoot := shellcomplete.NewRootCommand(app, g) @@ -95,7 +99,7 @@ func defineCommands( // messes up the order of the commands in the `--help` output. So to make the // placement of the `sso` subcommand not look too odd we place it at the // beginning of the list of commands. - ssoCmdRoot := sso.NewRootCommand(app, g, opts.Opener, opts.AuthServer) + ssoCmdRoot := sso.NewRootCommand(app, g, opener, authServer) aclCmdRoot := acl.NewRootCommand(app, g) aclCreate := acl.NewCreateCommand(aclCmdRoot.CmdClause, g, m) @@ -121,7 +125,7 @@ func defineCommands( backendList := backend.NewListCommand(backendCmdRoot.CmdClause, g, m) backendUpdate := backend.NewUpdateCommand(backendCmdRoot.CmdClause, g, m) computeCmdRoot := compute.NewRootCommand(app, g) - computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, g, opts.Versioners.WasmTools) + computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, g, versioners.WasmTools) computeDeploy := compute.NewDeployCommand(computeCmdRoot.CmdClause, g) computeHashFiles := compute.NewHashFilesCommand(computeCmdRoot.CmdClause, g, computeBuild) computeHashsum := compute.NewHashsumCommand(computeCmdRoot.CmdClause, g, computeBuild) @@ -129,7 +133,7 @@ func defineCommands( computeMetadata := compute.NewMetadataCommand(computeCmdRoot.CmdClause, g) computePack := compute.NewPackCommand(computeCmdRoot.CmdClause, g, m) computePublish := compute.NewPublishCommand(computeCmdRoot.CmdClause, g, computeBuild, computeDeploy) - computeServe := compute.NewServeCommand(computeCmdRoot.CmdClause, g, computeBuild, opts.Versioners.Viceroy) + computeServe := compute.NewServeCommand(computeCmdRoot.CmdClause, g, computeBuild, versioners.Viceroy) computeUpdate := compute.NewUpdateCommand(computeCmdRoot.CmdClause, g, m) computeValidate := compute.NewValidateCommand(computeCmdRoot.CmdClause, g) configCmdRoot := config.NewRootCommand(app, g) @@ -343,12 +347,12 @@ func defineCommands( popCmdRoot := pop.NewRootCommand(app, g) productsCmdRoot := products.NewRootCommand(app, g, m) profileCmdRoot := profile.NewRootCommand(app, g) - profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, ssoCmdRoot) + profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(factory), g, ssoCmdRoot) profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, g) profileList := profile.NewListCommand(profileCmdRoot.CmdClause, g) profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, g) profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, g) - profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(opts.APIClient), g, ssoCmdRoot) + profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(factory), g, ssoCmdRoot) purgeCmdRoot := purge.NewRootCommand(app, g, m) rateLimitCmdRoot := ratelimit.NewRootCommand(app, g) rateLimitCreate := ratelimit.NewCreateCommand(rateLimitCmdRoot.CmdClause, g, m) @@ -432,7 +436,7 @@ func defineCommands( tlsSubscriptionDescribe := tlssubscription.NewDescribeCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) tlsSubscriptionList := tlssubscription.NewListCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) tlsSubscriptionUpdate := tlssubscription.NewUpdateCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) - updateRoot := update.NewRootCommand(app, opts.ConfigPath, opts.Versioners.CLI, g) + updateRoot := update.NewRootCommand(app, g.ConfigPath, versioners.CLI, g) userCmdRoot := user.NewRootCommand(app, g) userCreate := user.NewCreateCommand(userCmdRoot.CmdClause, g, m) userDelete := user.NewDeleteCommand(userCmdRoot.CmdClause, g, m) @@ -458,7 +462,7 @@ func defineCommands( vclSnippetDescribe := snippet.NewDescribeCommand(vclSnippetCmdRoot.CmdClause, g, m) vclSnippetList := snippet.NewListCommand(vclSnippetCmdRoot.CmdClause, g, m) vclSnippetUpdate := snippet.NewUpdateCommand(vclSnippetCmdRoot.CmdClause, g, m) - versionCmdRoot := version.NewRootCommand(app, opts.Versioners.Viceroy) + versionCmdRoot := version.NewRootCommand(app, versioners.Viceroy) whoamiCmdRoot := whoami.NewRootCommand(app, g) return []cmd.Command{ diff --git a/pkg/app/run.go b/pkg/app/run.go index 6f43916c9..4b7f8eab0 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -1,8 +1,10 @@ package app import ( + "encoding/json" "fmt" "io" + "net/http" "os" "slices" "strconv" @@ -11,10 +13,13 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/kingpin" + "github.com/fatih/color" + "github.com/skratchdot/open-golang/open" "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/cli/pkg/commands/compute" "github.com/fastly/cli/pkg/commands/update" "github.com/fastly/cli/pkg/commands/version" "github.com/fastly/cli/pkg/config" @@ -26,20 +31,169 @@ import ( "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/profile" "github.com/fastly/cli/pkg/revision" + "github.com/fastly/cli/pkg/sync" "github.com/fastly/cli/pkg/text" ) -// Run constructs the application including all of the subcommands, parses the +func Run(args []string, stdin io.Reader) error { + opts, err := Init(args, stdin) + if err != nil { + return fmt.Errorf("failed to initialise application: %w", err) + } + return Exec(opts) +} + +// Init constructs all the required objects and data for Exec(). +// +// NOTE: We define as a package level variable so we can mock output for tests. +var Init = func(args []string, stdin io.Reader) (RunOpts, error) { + // Parse the arguments provided by the user via the command-line interface. + args = args[1:] + + // Define a HTTP client that will be used for making arbitrary HTTP requests. + httpClient := &http.Client{Timeout: time.Minute * 2} + + // Define the standard input/output streams. + var ( + in io.Reader = stdin + out io.Writer = sync.NewWriter(color.Output) + ) + + // Read relevant configuration options from the user's environment. + var e config.Environment + e.Read(env.Parse(os.Environ())) + + // Identify verbose flag early (before Kingpin parser has executed) so we can + // print additional output related to the CLI configuration. + var verboseOutput bool + for _, seg := range args { + if seg == "-v" || seg == "--verbose" { + verboseOutput = true + } + } + + // Identify auto-yes/non-interactive flag early (before Kingpin parser has + // executed) so we can handle the interactive prompts appropriately with + // regards to processing the CLI configuration. + var autoYes, nonInteractive bool + for _, seg := range args { + if seg == "-y" || seg == "--auto-yes" { + autoYes = true + } + if seg == "-i" || seg == "--non-interactive" { + nonInteractive = true + } + } + + // Extract a subset of configuration options from the local app directory. + var cfg config.File + cfg.SetAutoYes(autoYes) + cfg.SetNonInteractive(nonInteractive) + if err := cfg.Read(config.FilePath, in, out, fsterr.Log, verboseOutput); err != nil { + return RunOpts{}, err + } + + // Extract user's project configuration from the fastly.toml manifest. + var md manifest.Data + md.File.Args = args + md.File.SetErrLog(fsterr.Log) + md.File.SetOutput(out) + + // NOTE: We skip handling the error because not all commands relate to Compute. + _ = md.File.Read(manifest.Filename) + + // Configure authentication inputs. + // We do this here so that we can mock the values in our test suite. + req, err := http.NewRequest(http.MethodGet, auth.OIDCMetadata, nil) + if err != nil { + return RunOpts{}, fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) + } + resp, err := httpClient.Do(req) + if err != nil { + return RunOpts{}, fmt.Errorf("failed to request OpenID Connect .well-known metadata: %w", err) + } + openIDConfig, err := io.ReadAll(resp.Body) + if err != nil { + return RunOpts{}, fmt.Errorf("failed to read OpenID Connect .well-known metadata: %w", err) + } + _ = resp.Body.Close() + var wellknown auth.WellKnownEndpoints + err = json.Unmarshal(openIDConfig, &wellknown) + if err != nil { + return RunOpts{}, fmt.Errorf("failed to unmarshal OpenID Connect .well-known metadata: %w", err) + } + result := make(chan auth.AuthorizationResult) + router := http.NewServeMux() + authServer := &auth.Server{ + DebugMode: e.DebugMode, + HTTPClient: httpClient, + Result: result, + Router: router, + WellKnownEndpoints: wellknown, + } + router.HandleFunc("/callback", authServer.HandleCallback()) + + factory := func(token, endpoint string, debugMode bool) (api.Interface, error) { + client, err := fastly.NewClientForEndpoint(token, endpoint) + if debugMode { + client.DebugMode = true + } + return client, err + } + + versioners := Versioners{ + CLI: github.New(github.Opts{ + HTTPClient: httpClient, + Org: "fastly", + Repo: "cli", + Binary: "fastly", + }), + Viceroy: github.New(github.Opts{ + HTTPClient: httpClient, + Org: "fastly", + Repo: "viceroy", + Binary: "viceroy", + Version: md.File.LocalServer.ViceroyVersion, + }), + WasmTools: github.New(github.Opts{ + HTTPClient: httpClient, + Org: "bytecodealliance", + Repo: "wasm-tools", + Binary: "wasm-tools", + External: true, + Nested: true, + }), + } + + return RunOpts{ + APIClientFactory: factory, + Args: args, + AuthServer: authServer, + ConfigFile: cfg, + ConfigPath: config.FilePath, + Env: e, + ErrLog: fsterr.Log, + ExecuteWasmTools: compute.ExecuteWasmTools, + HTTPClient: httpClient, + Manifest: &md, + Opener: open.Run, + Stdin: in, + Stdout: out, + Versioners: versioners, + }, nil +} + +// Exec constructs the application including all of the subcommands, parses the // args, invokes the client factory with the token to create a Fastly API // client, and executes the chosen command, using the provided io.Reader and // io.Writer for input and output, respectively. In the real CLI, func main is // just a simple shim to this function; it exists to make end-to-end testing of // commands easier/possible. // -// The Run helper should NOT output any error-related information to the out +// The Exec helper should NOT output any error-related information to the out // io.Writer. All error-related information should be encoded into an error type // and returned to the caller. This includes usage text. -func Run(opts RunOpts) error { +func Exec(opts RunOpts) error { // The g will hold generally-applicable configuration parameters // from a variety of sources, and is provided to each concrete command. g := global.Data{ @@ -54,8 +208,8 @@ func Run(opts RunOpts) error { } app := configureKingpin(opts.Stdout, &g) - commands := defineCommands(app, &g, *opts.Manifest, opts) - command, commandName, err := processCommandInput(opts, app, &g, commands) + commands := defineCommands(app, &g, *opts.Manifest, opts.Opener, opts.AuthServer, opts.Versioners, opts.APIClientFactory) + command, commandName, err := processCommandInput(opts.Args, opts.Stdout, app, &g, commands) if err != nil { return err } @@ -101,7 +255,7 @@ func Run(opts RunOpts) error { return fmt.Errorf("failed to process token: %w", err) } - g.APIClient, g.RTSClient, err = configureClients(token, apiEndpoint, opts.APIClient, g.Flags.Debug) + g.APIClient, g.RTSClient, err = configureClients(token, apiEndpoint, opts.APIClientFactory, g.Flags.Debug) if err != nil { g.ErrLog.Add(err) return fmt.Errorf("error constructing client: %w", err) @@ -113,10 +267,10 @@ func Run(opts RunOpts) error { return command.Exec(opts.Stdin, opts.Stdout) } -// RunOpts represent arguments to Run(). +// RunOpts represent arguments we can mock at runtime. type RunOpts struct { - // APIClient is a factory function for creating an api.Interface type. - APIClient APIClientFactory + // APIClientFactory is a factory function for creating an api.Interface type. + APIClientFactory APIClientFactory // Args are the command line arguments provided by the user. Args []string // AuthServer is an instance of the authentication server type. diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index 112f16151..e3b2e1322 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -122,9 +122,10 @@ whoami outC <- buf.String() }() - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return testutil.NewRunOpts(testcase.Args, &stdout), nil + } + err := app.Run(testcase.Args, nil) if err != nil { errors.Deduce(err).Print(&stderr) } diff --git a/pkg/app/usage.go b/pkg/app/usage.go index d146bef4d..5b41acc51 100644 --- a/pkg/app/usage.go +++ b/pkg/app/usage.go @@ -208,7 +208,8 @@ const VerboseUsageTemplate = `{{define "FormatCommands" -}} // processing the incoming command request from the user, as well as handling // the various places where help output can be displayed. func processCommandInput( - opts RunOpts, + args []string, + out io.Writer, app *kingpin.Application, g *global.Data, commands []cmd.Command, @@ -218,18 +219,18 @@ func processCommandInput( // Therefore, we have to manually parse the args slice here to check for the // existence of `help --format json`, if present we print usage JSON and // exit early. - if cmd.ArgsIsHelpJSON(opts.Args) { + if cmd.ArgsIsHelpJSON(args) { j, err := UsageJSON(app) if err != nil { g.ErrLog.Add(err) return command, cmdName, err } - fmt.Fprintf(opts.Stdout, "%s", j) - return command, strings.Join(opts.Args, ""), nil + fmt.Fprintf(out, "%s", j) + return command, strings.Join(args, ""), nil } // Use partial application to generate help output function. - help := displayHelp(g.ErrLog, opts.Args, app, opts.Stdout, io.Discard) + help := displayHelp(g.ErrLog, args, app, out, io.Discard) // Handle parse errors and display contextual usage if possible. Due to bugs // and an obsession for lots of output side-effects in the kingpin.Parse @@ -250,14 +251,14 @@ func processCommandInput( // But it's useful to have it implemented so it's ready to roll when we do. var vars map[string]any - if cmd.IsVerboseAndQuiet(opts.Args) { + if cmd.IsVerboseAndQuiet(args) { return command, cmdName, fsterr.RemediationError{ Inner: errors.New("--verbose and --quiet flag provided"), Remediation: "Either remove both --verbose and --quiet flags, or one of them.", } } - if cmd.IsHelpFlagOnly(opts.Args) && len(opts.Args) == 1 { + if cmd.IsHelpFlagOnly(args) && len(args) == 1 { return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: help(vars, nil), @@ -284,17 +285,17 @@ func processCommandInput( // // ctx.SelectedCommand will be nil if only a flag like --verbose or -v is // provided but with no actual command set so we check with IsGlobalFlagsOnly. - noargs := len(opts.Args) == 0 - globalFlagsOnly := cmd.IsGlobalFlagsOnly(opts.Args) - ctx, err := app.ParseContext(opts.Args) - if err != nil && !cmd.IsCompletion(opts.Args) || noargs || globalFlagsOnly { + noargs := len(args) == 0 + globalFlagsOnly := cmd.IsGlobalFlagsOnly(args) + ctx, err := app.ParseContext(args) + if err != nil && !cmd.IsCompletion(args) || noargs || globalFlagsOnly { if noargs || globalFlagsOnly { err = fmt.Errorf("command not specified") } return command, cmdName, help(vars, err) } - if len(opts.Args) == 1 && opts.Args[0] == "--" { + if len(args) == 1 && args[0] == "--" { return command, cmdName, fsterr.RemediationError{ Inner: errors.New("-- is invalid input when not followed by a positional argument"), Remediation: "If looking for help output try: `fastly help` for full command list or `fastly --help` for command summary.", @@ -310,14 +311,14 @@ func processCommandInput( // completion flag, as that depends on kingpin.Parse() being called, and so // the `ctx` is otherwise empty. var found bool - if !noargs && !globalFlagsOnly && !cmd.IsHelpOnly(opts.Args) && !cmd.IsHelpFlagOnly(opts.Args) && !cmd.IsCompletion(opts.Args) && !cmd.IsCompletionScript(opts.Args) { + if !noargs && !globalFlagsOnly && !cmd.IsHelpOnly(args) && !cmd.IsHelpFlagOnly(args) && !cmd.IsCompletion(args) && !cmd.IsCompletionScript(args) { command, found = cmd.Select(ctx.SelectedCommand.FullCommand(), commands) if !found { return command, cmdName, help(vars, err) } } - if cmd.ContextHasHelpFlag(ctx) && !cmd.IsHelpFlagOnly(opts.Args) { + if cmd.ContextHasHelpFlag(ctx) && !cmd.IsHelpFlagOnly(args) { return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: help(vars, nil), @@ -344,21 +345,21 @@ func processCommandInput( // caller passes --completion-bash because adding a command to the arguments // list in that scenario would cause Kingpin logic to fail (as it expects the // flag to be used on its own). - if cmd.IsCompletionScript(opts.Args) { - opts.Args = append(opts.Args, "shellcomplete") + if cmd.IsCompletionScript(args) { + args = append(args, "shellcomplete") } - cmdName, err = app.Parse(opts.Args) + cmdName, err = app.Parse(args) if err != nil { return command, "", help(vars, err) } // Restore output writers - app.Writers(opts.Stdout, io.Discard) + app.Writers(out, io.Discard) // Kingpin generates shell completion as a side-effect of kingpin.Parse() so // we allow it to call os.Exit, only if a completion flag is present. - if cmd.IsCompletion(opts.Args) || cmd.IsCompletionScript(opts.Args) { + if cmd.IsCompletion(args) || cmd.IsCompletionScript(args) { app.Terminate(os.Exit) return command, "shell-autocomplete", nil } @@ -371,14 +372,14 @@ func processCommandInput( return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: fsterr.RemediationError{ - Prefix: useFullHelpOutput(app, opts).String(), + Prefix: useFullHelpOutput(app, args, out).String(), }, } } // Catch scenario where user wants to view help with the following format: // fastly --help - if cmd.IsHelpFlagOnly(opts.Args) { + if cmd.IsHelpFlagOnly(args) { return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: help(vars, nil), @@ -388,16 +389,16 @@ func processCommandInput( return command, cmdName, nil } -func useFullHelpOutput(app *kingpin.Application, opts RunOpts) *bytes.Buffer { +func useFullHelpOutput(app *kingpin.Application, args []string, out io.Writer) *bytes.Buffer { var buf bytes.Buffer app.Writers(&buf, io.Discard) - _, _ = app.Parse(opts.Args) - app.Writers(opts.Stdout, io.Discard) + _, _ = app.Parse(args) + app.Writers(out, io.Discard) // The full-fat output of `fastly help` should have a hint at the bottom // for more specific help. Unfortunately I don't know of a better way to // distinguish `fastly help` from e.g. `fastly help pops` than this check. - if len(opts.Args) > 0 && opts.Args[len(opts.Args)-1] == "help" { + if len(args) > 0 && args[len(args)-1] == "help" { fmt.Fprintln(&buf, "\nFor help on a specific command, try e.g.") fmt.Fprintln(&buf, "") fmt.Fprintln(&buf, "\tfastly help profile") diff --git a/pkg/commands/acl/acl_test.go b/pkg/commands/acl/acl_test.go index 39804d2d6..f481faaf4 100644 --- a/pkg/commands/acl/acl_test.go +++ b/pkg/commands/acl/acl_test.go @@ -2,6 +2,7 @@ package acl_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -87,9 +88,12 @@ func TestACLCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) @@ -163,9 +167,12 @@ func TestACLDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -225,9 +232,12 @@ func TestACLDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -291,9 +301,12 @@ func TestACLList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -381,9 +394,12 @@ func TestACLUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/aclentry/aclentry_test.go b/pkg/commands/aclentry/aclentry_test.go index 6bc74c719..7be64aa8f 100644 --- a/pkg/commands/aclentry/aclentry_test.go +++ b/pkg/commands/aclentry/aclentry_test.go @@ -2,6 +2,7 @@ package aclentry_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -76,9 +77,12 @@ func TestACLEntryCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -129,9 +133,12 @@ func TestACLEntryDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -180,9 +187,12 @@ func TestACLEntryDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -326,9 +336,12 @@ func TestACLEntryList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -455,9 +468,12 @@ func TestACLEntryUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/authtoken/authtoken_test.go b/pkg/commands/authtoken/authtoken_test.go index be8195838..a19a5760a 100644 --- a/pkg/commands/authtoken/authtoken_test.go +++ b/pkg/commands/authtoken/authtoken_test.go @@ -3,6 +3,7 @@ package authtoken_test import ( "bytes" "fmt" + "io" "os" "testing" @@ -69,9 +70,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -162,9 +166,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -198,9 +205,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -295,9 +305,12 @@ func TestList(t *testing.T) { }() } var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/backend/backend_test.go b/pkg/commands/backend/backend_test.go index 97b087867..5e8d390f1 100644 --- a/pkg/commands/backend/backend_test.go +++ b/pkg/commands/backend/backend_test.go @@ -3,6 +3,7 @@ package backend_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -130,9 +131,12 @@ func TestBackendCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -211,9 +215,12 @@ func TestBackendList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) if testcase.WantError == "" { testutil.AssertString(t, testcase.WantOutput, stdout.String()) @@ -250,9 +257,12 @@ func TestBackendDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -291,9 +301,12 @@ func TestBackendUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -330,9 +343,12 @@ func TestBackendDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/compute/build_test.go b/pkg/commands/compute/build_test.go index 65ce4b54d..11b59dc3b 100644 --- a/pkg/commands/compute/build_test.go +++ b/pkg/commands/compute/build_test.go @@ -2,6 +2,7 @@ package compute_test import ( "fmt" + "io" "os" "os/exec" "path/filepath" @@ -216,22 +217,23 @@ func TestBuildRust(t *testing.T) { }() var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - - if testcase.applicationConfig != nil { - opts.ConfigFile = *testcase.applicationConfig - } - opts.Versioners = app.Versioners{ - WasmTools: mock.AssetVersioner{ - AssetVersion: "1.2.3", - BinaryFilename: wasmtoolsBinName, - DownloadOK: true, - DownloadedFile: latestDownloaded, - InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install - }, + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + if testcase.applicationConfig != nil { + opts.ConfigFile = *testcase.applicationConfig + } + opts.Versioners = app.Versioners{ + WasmTools: mock.AssetVersioner{ + AssetVersion: "1.2.3", + BinaryFilename: wasmtoolsBinName, + DownloadOK: true, + DownloadedFile: latestDownloaded, + InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install + }, + } + return opts, nil } - - err = app.Run(opts) + err = app.Run(testcase.args, nil) t.Log(stdout.String()) @@ -408,22 +410,23 @@ func TestBuildGo(t *testing.T) { }() var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - - if testcase.applicationConfig != nil { - opts.ConfigFile = *testcase.applicationConfig - } - opts.Versioners = app.Versioners{ - WasmTools: mock.AssetVersioner{ - AssetVersion: "1.2.3", - BinaryFilename: wasmtoolsBinName, - DownloadOK: true, - DownloadedFile: latestDownloaded, - InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install - }, + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + if testcase.applicationConfig != nil { + opts.ConfigFile = *testcase.applicationConfig + } + opts.Versioners = app.Versioners{ + WasmTools: mock.AssetVersioner{ + AssetVersion: "1.2.3", + BinaryFilename: wasmtoolsBinName, + DownloadOK: true, + DownloadedFile: latestDownloaded, + InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install + }, + } + return opts, nil } - - err = app.Run(opts) + err = app.Run(testcase.args, nil) t.Log(stdout.String()) @@ -605,18 +608,20 @@ func TestBuildJavaScript(t *testing.T) { } var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.Versioners = app.Versioners{ - WasmTools: mock.AssetVersioner{ - AssetVersion: "1.2.3", - BinaryFilename: wasmtoolsBinName, - DownloadOK: true, - DownloadedFile: latestDownloaded, - InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install - }, + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.Versioners = app.Versioners{ + WasmTools: mock.AssetVersioner{ + AssetVersion: "1.2.3", + BinaryFilename: wasmtoolsBinName, + DownloadOK: true, + DownloadedFile: latestDownloaded, + InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install + }, + } + return opts, nil } - - err = app.Run(opts) + err = app.Run(testcase.args, nil) t.Log(stdout.String()) @@ -798,18 +803,20 @@ func TestBuildAssemblyScript(t *testing.T) { } var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.Versioners = app.Versioners{ - WasmTools: mock.AssetVersioner{ - AssetVersion: "1.2.3", - BinaryFilename: wasmtoolsBinName, - DownloadOK: true, - DownloadedFile: latestDownloaded, - InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install - }, + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.Versioners = app.Versioners{ + WasmTools: mock.AssetVersioner{ + AssetVersion: "1.2.3", + BinaryFilename: wasmtoolsBinName, + DownloadOK: true, + DownloadedFile: latestDownloaded, + InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install + }, + } + return opts, nil } - - err = app.Run(opts) + err = app.Run(testcase.args, nil) t.Log(stdout.String()) @@ -1008,19 +1015,21 @@ func TestBuildOther(t *testing.T) { } var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.Stdin = strings.NewReader(testcase.stdin) // NOTE: build only has one prompt when dealing with a custom build - opts.Versioners = app.Versioners{ - WasmTools: mock.AssetVersioner{ - AssetVersion: "1.2.3", - BinaryFilename: wasmtoolsBinName, - DownloadOK: true, - DownloadedFile: latestDownloaded, - InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install - }, + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.Stdin = strings.NewReader(testcase.stdin) // NOTE: build only has one prompt when dealing with a custom build + opts.Versioners = app.Versioners{ + WasmTools: mock.AssetVersioner{ + AssetVersion: "1.2.3", + BinaryFilename: wasmtoolsBinName, + DownloadOK: true, + DownloadedFile: latestDownloaded, + InstallFilePath: wasmtoolsBinPath, // avoid overwriting developer's actual wasm-tools install + }, + } + return opts, nil } - - err = app.Run(opts) + err = app.Run(testcase.args, nil) t.Log(stdout.String()) diff --git a/pkg/commands/compute/deploy_test.go b/pkg/commands/compute/deploy_test.go index 47b84c81a..448410327 100644 --- a/pkg/commands/compute/deploy_test.go +++ b/pkg/commands/compute/deploy_test.go @@ -2154,7 +2154,7 @@ func TestDeploy(t *testing.T) { var stdout threadsafe.Buffer opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) + opts.APIClientFactory = mock.APIClient(testcase.api) if testcase.httpClientRes != nil || testcase.httpClientErr != nil { opts.HTTPClient = mock.HTMLClient(testcase.httpClientRes, testcase.httpClientErr) @@ -2187,7 +2187,10 @@ func TestDeploy(t *testing.T) { // Call `app.Run()` and wait for response go func() { - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.args, nil) done <- true }() @@ -2213,7 +2216,10 @@ func TestDeploy(t *testing.T) { stdin = testcase.stdin[0] } opts.Stdin = strings.NewReader(stdin) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.args, nil) } t.Log(stdout.String()) diff --git a/pkg/commands/compute/init_test.go b/pkg/commands/compute/init_test.go index 95e99321a..f427fb0f3 100644 --- a/pkg/commands/compute/init_test.go +++ b/pkg/commands/compute/init_test.go @@ -3,6 +3,7 @@ package compute_test import ( "bytes" "errors" + "io" "os" "path/filepath" "strings" @@ -358,15 +359,17 @@ func TestInit(t *testing.T) { }() var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.ConfigFile = testcase.configFile + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.ConfigFile = testcase.configFile - // we need to define stdin as the init process prompts the user multiple - // times, but we don't need to provide any values as all our prompts will - // fallback to default values if the input is unrecognised. - opts.Stdin = strings.NewReader(testcase.stdin) - - err = app.Run(opts) + // we need to define stdin as the init process prompts the user multiple + // times, but we don't need to provide any values as all our prompts will + // fallback to default values if the input is unrecognised. + opts.Stdin = strings.NewReader(testcase.stdin) + return opts, nil + } + err = app.Run(testcase.args, nil) t.Log(stdout.String()) diff --git a/pkg/commands/compute/metadata_test.go b/pkg/commands/compute/metadata_test.go index def87b21c..1083469a9 100644 --- a/pkg/commands/compute/metadata_test.go +++ b/pkg/commands/compute/metadata_test.go @@ -2,6 +2,7 @@ package compute_test import ( "bytes" + "io" "os" "path/filepath" "strings" @@ -147,7 +148,10 @@ func TestMetadata(t *testing.T) { }, } - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) t.Log(stdout.String()) diff --git a/pkg/commands/compute/pack_test.go b/pkg/commands/compute/pack_test.go index 85c59c240..1757c6aa3 100644 --- a/pkg/commands/compute/pack_test.go +++ b/pkg/commands/compute/pack_test.go @@ -2,6 +2,7 @@ package compute_test import ( "bytes" + "io" "os" "path/filepath" "testing" @@ -81,8 +82,10 @@ func TestPack(t *testing.T) { }() var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return testutil.NewRunOpts(testcase.args, &stdout), nil + } + err = app.Run(testcase.args, nil) t.Log(stdout.String()) diff --git a/pkg/commands/compute/update_test.go b/pkg/commands/compute/update_test.go index 59dd71352..dd5426995 100644 --- a/pkg/commands/compute/update_test.go +++ b/pkg/commands/compute/update_test.go @@ -3,6 +3,7 @@ package compute_test import ( "bytes" "fmt" + "io" "os" "path/filepath" "testing" @@ -75,9 +76,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err = app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) for _, s := range testcase.WantOutputs { testutil.AssertStringContains(t, stdout.String(), s) diff --git a/pkg/commands/compute/validate_test.go b/pkg/commands/compute/validate_test.go index 386730434..ff331dc0d 100644 --- a/pkg/commands/compute/validate_test.go +++ b/pkg/commands/compute/validate_test.go @@ -2,6 +2,7 @@ package compute_test import ( "bytes" + "io" "os" "path/filepath" "testing" @@ -53,8 +54,10 @@ func TestValidate(t *testing.T) { }() var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return testutil.NewRunOpts(testcase.Args, &stdout), nil + } + err = app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/config/config_test.go b/pkg/commands/config/config_test.go index c1d0e3054..2afd73c30 100644 --- a/pkg/commands/config/config_test.go +++ b/pkg/commands/config/config_test.go @@ -2,6 +2,7 @@ package config_test import ( "bytes" + "io" "os" "path/filepath" "testing" @@ -70,10 +71,13 @@ func TestConfig(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - opts.ConfigPath = configPath - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + opts.ConfigPath = configPath + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/configstore/configstore_test.go b/pkg/commands/configstore/configstore_test.go index f657fb9ac..4e79579f2 100644 --- a/pkg/commands/configstore/configstore_test.go +++ b/pkg/commands/configstore/configstore_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "testing" "time" @@ -74,12 +75,12 @@ func TestCreateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -137,12 +138,12 @@ func TestDeleteStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -241,12 +242,12 @@ func TestDescribeStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -309,12 +310,12 @@ func TestListStoresCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -375,12 +376,12 @@ func TestListStoreServicesCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -446,12 +447,12 @@ func TestUpdateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) diff --git a/pkg/commands/configstoreentry/configstoreentry_test.go b/pkg/commands/configstoreentry/configstoreentry_test.go index 0d94d2337..cfdafca0f 100644 --- a/pkg/commands/configstoreentry/configstoreentry_test.go +++ b/pkg/commands/configstoreentry/configstoreentry_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "testing" "time" @@ -80,12 +81,12 @@ func TestCreateEntryCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -198,14 +199,13 @@ SUCCESS: Deleted all keys from Config Store '%s' testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) t.Log(stdout.String()) - testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -277,12 +277,12 @@ func TestDescribeEntryCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -343,12 +343,12 @@ func TestListEntriesCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -417,12 +417,12 @@ func TestUpdateEntryCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) diff --git a/pkg/commands/dictionary/dictionary_test.go b/pkg/commands/dictionary/dictionary_test.go index 7aab4f36e..8157c900c 100644 --- a/pkg/commands/dictionary/dictionary_test.go +++ b/pkg/commands/dictionary/dictionary_test.go @@ -3,6 +3,7 @@ package dictionary_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -56,9 +57,12 @@ func TestDictionaryDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -117,9 +121,12 @@ func TestDictionaryCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -161,9 +168,12 @@ func TestDeleteDictionary(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -203,9 +213,12 @@ func TestListDictionary(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -290,9 +303,12 @@ func TestUpdateDictionary(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) diff --git a/pkg/commands/dictionaryentry/dictionaryitem_test.go b/pkg/commands/dictionaryentry/dictionaryitem_test.go index 9422b2a05..242a9f9d1 100644 --- a/pkg/commands/dictionaryentry/dictionaryitem_test.go +++ b/pkg/commands/dictionaryentry/dictionaryitem_test.go @@ -3,6 +3,7 @@ package dictionaryentry_test import ( "bytes" "errors" + "io" "os" "strings" "testing" @@ -47,9 +48,12 @@ func TestDictionaryItemDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -172,9 +176,12 @@ func TestDictionaryItemsList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -209,9 +216,12 @@ func TestDictionaryItemCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -285,9 +295,12 @@ func TestDictionaryItemUpdate(t *testing.T) { } var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -322,9 +335,12 @@ func TestDictionaryItemDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) diff --git a/pkg/commands/domain/domain_test.go b/pkg/commands/domain/domain_test.go index 3f6bc19c3..2e152d742 100644 --- a/pkg/commands/domain/domain_test.go +++ b/pkg/commands/domain/domain_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestDomainCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -109,9 +113,12 @@ func TestDomainList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -146,9 +153,12 @@ func TestDomainDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -194,9 +204,12 @@ func TestDomainUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -233,9 +246,12 @@ func TestDomainDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -318,9 +334,12 @@ func TestDomainValidate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/healthcheck/healthcheck_test.go b/pkg/commands/healthcheck/healthcheck_test.go index e005d5268..3f2fcaa2d 100644 --- a/pkg/commands/healthcheck/healthcheck_test.go +++ b/pkg/commands/healthcheck/healthcheck_test.go @@ -3,6 +3,7 @@ package healthcheck_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -50,9 +51,12 @@ func TestHealthCheckCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -120,9 +124,12 @@ func TestHealthCheckList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -162,9 +169,12 @@ func TestHealthCheckDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -214,9 +224,12 @@ func TestHealthCheckUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -258,9 +271,12 @@ func TestHealthCheckDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/ip/ip_test.go b/pkg/commands/ip/ip_test.go index 03b1cd074..da5ff3bb7 100644 --- a/pkg/commands/ip/ip_test.go +++ b/pkg/commands/ip/ip_test.go @@ -2,6 +2,7 @@ package ip_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -23,9 +24,12 @@ func TestAllIPs(t *testing.T) { }, nil }, } - opts := testutil.NewRunOpts(args, &stdout) - opts.APIClient = mock.APIClient(api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(args, &stdout) + opts.APIClientFactory = mock.APIClient(api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertNoError(t, err) testutil.AssertString(t, "\nIPv4\n\t00.123.45.6/78\n\nIPv6\n\t0a12:3b45::/67\n", stdout.String()) } diff --git a/pkg/commands/kvstore/kvstore_test.go b/pkg/commands/kvstore/kvstore_test.go index 2a85bc73f..fdfbc87c7 100644 --- a/pkg/commands/kvstore/kvstore_test.go +++ b/pkg/commands/kvstore/kvstore_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "testing" "time" @@ -75,12 +76,12 @@ func TestCreateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -138,12 +139,12 @@ func TestDeleteStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -216,12 +217,12 @@ func TestDescribeStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -286,12 +287,12 @@ func TestListStoresCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) diff --git a/pkg/commands/kvstoreentry/kvstoreentry_test.go b/pkg/commands/kvstoreentry/kvstoreentry_test.go index e096bd041..fbcf406d9 100644 --- a/pkg/commands/kvstoreentry/kvstoreentry_test.go +++ b/pkg/commands/kvstoreentry/kvstoreentry_test.go @@ -128,14 +128,15 @@ func TestCreateCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - - if testcase.Stdin != nil { - opts.Stdin = testcase.Stdin + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + if testcase.Stdin != nil { + opts.Stdin = testcase.Stdin + } + return opts, nil } - - err := app.Run(opts) + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) @@ -250,14 +251,13 @@ SUCCESS: Deleted all keys from KV Store '%s' testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) t.Log(stdout.String()) - testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -309,12 +309,12 @@ func TestDescribeCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -367,12 +367,12 @@ func TestListCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - - opts.APIClient = mock.APIClient(testcase.API) - - err := app.Run(opts) - + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/logging/azureblob/azureblob_integration_test.go b/pkg/commands/logging/azureblob/azureblob_integration_test.go index e981767a6..0caf19261 100644 --- a/pkg/commands/logging/azureblob/azureblob_integration_test.go +++ b/pkg/commands/logging/azureblob/azureblob_integration_test.go @@ -3,6 +3,7 @@ package azureblob_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -53,9 +54,12 @@ func TestBlobStorageCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -123,9 +127,12 @@ func TestBlobStorageList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -165,9 +172,12 @@ func TestBlobStorageDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -209,9 +219,12 @@ func TestBlobStorageUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -253,9 +266,12 @@ func TestBlobStorageDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/bigquery/bigquery_integration_test.go b/pkg/commands/logging/bigquery/bigquery_integration_test.go index bd5ef3116..324ea7c96 100644 --- a/pkg/commands/logging/bigquery/bigquery_integration_test.go +++ b/pkg/commands/logging/bigquery/bigquery_integration_test.go @@ -3,6 +3,7 @@ package bigquery_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestBigQueryCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestBigQueryList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestBigQueryDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestBigQueryUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestBigQueryDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go index ab5f12695..c4a3636f6 100644 --- a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go +++ b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go @@ -3,6 +3,7 @@ package cloudfiles_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -52,9 +53,12 @@ func TestCloudfilesCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -122,9 +126,12 @@ func TestCloudfilesList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -164,9 +171,12 @@ func TestCloudfilesDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -208,9 +218,12 @@ func TestCloudfilesUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -252,9 +265,12 @@ func TestCloudfilesDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/datadog/datadog_integration_test.go b/pkg/commands/logging/datadog/datadog_integration_test.go index 16c775831..bce6272b1 100644 --- a/pkg/commands/logging/datadog/datadog_integration_test.go +++ b/pkg/commands/logging/datadog/datadog_integration_test.go @@ -3,6 +3,7 @@ package datadog_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestDatadogCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestDatadogList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestDatadogDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestDatadogUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestDatadogDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go index 2c09443c1..14788c45a 100644 --- a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go +++ b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go @@ -3,6 +3,7 @@ package digitalocean_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -52,9 +53,12 @@ func TestDigitalOceanCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -122,9 +126,12 @@ func TestDigitalOceanList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -164,9 +171,12 @@ func TestDigitalOceanDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -208,9 +218,12 @@ func TestDigitalOceanUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -252,9 +265,12 @@ func TestDigitalOceanDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go index 4c8981fff..097f76681 100644 --- a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go +++ b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go @@ -3,6 +3,7 @@ package elasticsearch_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestElasticsearchCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestElasticsearchList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestElasticsearchDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestElasticsearchUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestElasticsearchDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/ftp/ftp_integration_test.go b/pkg/commands/logging/ftp/ftp_integration_test.go index 1ad0e76e7..9c723dce4 100644 --- a/pkg/commands/logging/ftp/ftp_integration_test.go +++ b/pkg/commands/logging/ftp/ftp_integration_test.go @@ -3,6 +3,7 @@ package ftp_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -52,9 +53,12 @@ func TestFTPCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -122,9 +126,12 @@ func TestFTPList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -164,9 +171,12 @@ func TestFTPDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -208,9 +218,12 @@ func TestFTPUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -252,9 +265,12 @@ func TestFTPDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/gcs/gcs_integration_test.go b/pkg/commands/logging/gcs/gcs_integration_test.go index bd753b94e..3059abbf4 100644 --- a/pkg/commands/logging/gcs/gcs_integration_test.go +++ b/pkg/commands/logging/gcs/gcs_integration_test.go @@ -3,6 +3,7 @@ package gcs_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -52,9 +53,12 @@ func TestGCSCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -122,9 +126,12 @@ func TestGCSList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -164,9 +171,12 @@ func TestGCSDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -208,9 +218,12 @@ func TestGCSUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -252,9 +265,12 @@ func TestGCSDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go index 30aac45f8..f830da83b 100644 --- a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go +++ b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go @@ -3,6 +3,7 @@ package googlepubsub_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestGooglePubSubCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestGooglePubSubList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestGooglePubSubDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestGooglePubSubUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestGooglePubSubDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/heroku/heroku_integration_test.go b/pkg/commands/logging/heroku/heroku_integration_test.go index 353060075..9d2f2646e 100644 --- a/pkg/commands/logging/heroku/heroku_integration_test.go +++ b/pkg/commands/logging/heroku/heroku_integration_test.go @@ -3,6 +3,7 @@ package heroku_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestHerokuCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestHerokuList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestHerokuDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestHerokuUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestHerokuDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go index ec3180d4d..5e83d4202 100644 --- a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go +++ b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go @@ -3,6 +3,7 @@ package honeycomb_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestHoneycombCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestHoneycombList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestHoneycombDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestHoneycombUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestHoneycombDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/https/https_integration_test.go b/pkg/commands/logging/https/https_integration_test.go index e66016c02..04375291c 100644 --- a/pkg/commands/logging/https/https_integration_test.go +++ b/pkg/commands/logging/https/https_integration_test.go @@ -3,6 +3,7 @@ package https_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestHTTPSCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestHTTPSList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestHTTPSDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestHTTPSUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestHTTPSDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/kafka/kafka_integration_test.go b/pkg/commands/logging/kafka/kafka_integration_test.go index 7b5ff907b..8a01f7b18 100644 --- a/pkg/commands/logging/kafka/kafka_integration_test.go +++ b/pkg/commands/logging/kafka/kafka_integration_test.go @@ -3,6 +3,7 @@ package kafka_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestKafkaCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestKafkaList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestKafkaDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -209,9 +219,12 @@ func TestKafkaUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -253,9 +266,12 @@ func TestKafkaDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/kinesis/kinesis_integration_test.go b/pkg/commands/logging/kinesis/kinesis_integration_test.go index 960152d8d..f44415567 100644 --- a/pkg/commands/logging/kinesis/kinesis_integration_test.go +++ b/pkg/commands/logging/kinesis/kinesis_integration_test.go @@ -3,6 +3,7 @@ package kinesis_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -86,9 +87,12 @@ func TestKinesisCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -156,9 +160,12 @@ func TestKinesisList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -198,9 +205,12 @@ func TestKinesisDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -242,9 +252,12 @@ func TestKinesisUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -286,9 +299,12 @@ func TestKinesisDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -305,7 +321,7 @@ func createKinesisOK(i *fastly.CreateKinesisInput) (*fastly.Kinesis, error) { }, nil } -func createKinesisError(i *fastly.CreateKinesisInput) (*fastly.Kinesis, error) { +func createKinesisError(_ *fastly.CreateKinesisInput) (*fastly.Kinesis, error) { return nil, errTest } @@ -340,7 +356,7 @@ func listKinesesOK(i *fastly.ListKinesisInput) ([]*fastly.Kinesis, error) { }, nil } -func listKinesesError(i *fastly.ListKinesisInput) ([]*fastly.Kinesis, error) { +func listKinesesError(_ *fastly.ListKinesisInput) ([]*fastly.Kinesis, error) { return nil, errTest } @@ -399,7 +415,7 @@ func getKinesisOK(i *fastly.GetKinesisInput) (*fastly.Kinesis, error) { }, nil } -func getKinesisError(i *fastly.GetKinesisInput) (*fastly.Kinesis, error) { +func getKinesisError(_ *fastly.GetKinesisInput) (*fastly.Kinesis, error) { return nil, errTest } @@ -433,14 +449,14 @@ func updateKinesisOK(i *fastly.UpdateKinesisInput) (*fastly.Kinesis, error) { }, nil } -func updateKinesisError(i *fastly.UpdateKinesisInput) (*fastly.Kinesis, error) { +func updateKinesisError(_ *fastly.UpdateKinesisInput) (*fastly.Kinesis, error) { return nil, errTest } -func deleteKinesisOK(i *fastly.DeleteKinesisInput) error { +func deleteKinesisOK(_ *fastly.DeleteKinesisInput) error { return nil } -func deleteKinesisError(i *fastly.DeleteKinesisInput) error { +func deleteKinesisError(_ *fastly.DeleteKinesisInput) error { return errTest } diff --git a/pkg/commands/logging/loggly/loggly_integration_test.go b/pkg/commands/logging/loggly/loggly_integration_test.go index 25bf6b2e2..6f63e3843 100644 --- a/pkg/commands/logging/loggly/loggly_integration_test.go +++ b/pkg/commands/logging/loggly/loggly_integration_test.go @@ -3,6 +3,7 @@ package loggly_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestLogglyCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestLogglyList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestLogglyDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestLogglyUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestLogglyDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go index 120dbe1de..2b2bc1cf0 100644 --- a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go +++ b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go @@ -3,6 +3,7 @@ package logshuttle_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestLogshuttleCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestLogshuttleList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestLogshuttleDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestLogshuttleUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestLogshuttleDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/newrelic/newrelic_test.go b/pkg/commands/logging/newrelic/newrelic_test.go index 43c910180..6799e9ce0 100644 --- a/pkg/commands/logging/newrelic/newrelic_test.go +++ b/pkg/commands/logging/newrelic/newrelic_test.go @@ -2,6 +2,7 @@ package newrelic_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -75,9 +76,12 @@ func TestNewRelicCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -150,9 +154,12 @@ func TestNewRelicDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -212,9 +219,12 @@ func TestNewRelicDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -278,9 +288,12 @@ func TestNewRelicList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -361,9 +374,12 @@ func TestNewRelicUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go index 96cbc485b..755397624 100644 --- a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go +++ b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go @@ -2,6 +2,7 @@ package newrelicotlp_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -75,9 +76,12 @@ func TestNewRelicOTLPCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -150,9 +154,12 @@ func TestNewRelicOTLPDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -212,9 +219,12 @@ func TestNewRelicDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -278,9 +288,12 @@ func TestNewRelicList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -361,9 +374,12 @@ func TestNewRelicUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/logging/openstack/openstack_integration_test.go b/pkg/commands/logging/openstack/openstack_integration_test.go index 97984613c..e61061a6d 100644 --- a/pkg/commands/logging/openstack/openstack_integration_test.go +++ b/pkg/commands/logging/openstack/openstack_integration_test.go @@ -3,6 +3,7 @@ package openstack_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -52,9 +53,12 @@ func TestOpenstackCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -122,9 +126,12 @@ func TestOpenstackList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -164,9 +171,12 @@ func TestOpenstackDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -208,9 +218,12 @@ func TestOpenstackUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -252,9 +265,12 @@ func TestOpenstackDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/papertrail/papertrail_integration_test.go b/pkg/commands/logging/papertrail/papertrail_integration_test.go index c41540c42..2e153eb59 100644 --- a/pkg/commands/logging/papertrail/papertrail_integration_test.go +++ b/pkg/commands/logging/papertrail/papertrail_integration_test.go @@ -3,6 +3,7 @@ package papertrail_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestPapertrailCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestPapertrailList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestPapertrailDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestPapertrailUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestPapertrailDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/s3/s3_integration_test.go b/pkg/commands/logging/s3/s3_integration_test.go index 3c5ce2e51..ef1f340e3 100644 --- a/pkg/commands/logging/s3/s3_integration_test.go +++ b/pkg/commands/logging/s3/s3_integration_test.go @@ -3,6 +3,7 @@ package s3_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -102,9 +103,12 @@ func TestS3Create(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -172,9 +176,12 @@ func TestS3List(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -214,9 +221,12 @@ func TestS3Describe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -267,9 +277,12 @@ func TestS3Update(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -311,9 +324,12 @@ func TestS3Delete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/scalyr/scalyr_integration_test.go b/pkg/commands/logging/scalyr/scalyr_integration_test.go index f6e9541a0..530d6e3dc 100644 --- a/pkg/commands/logging/scalyr/scalyr_integration_test.go +++ b/pkg/commands/logging/scalyr/scalyr_integration_test.go @@ -3,6 +3,7 @@ package scalyr_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -53,9 +54,12 @@ func TestScalyrCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -123,9 +127,12 @@ func TestScalyrList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -165,9 +172,12 @@ func TestScalyrDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -209,9 +219,12 @@ func TestScalyrUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -253,9 +266,12 @@ func TestScalyrDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/sftp/sftp_integration_test.go b/pkg/commands/logging/sftp/sftp_integration_test.go index 983c2f47a..7efeddda2 100644 --- a/pkg/commands/logging/sftp/sftp_integration_test.go +++ b/pkg/commands/logging/sftp/sftp_integration_test.go @@ -3,6 +3,7 @@ package sftp_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -52,9 +53,12 @@ func TestSFTPCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -122,9 +126,12 @@ func TestSFTPList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -164,9 +171,12 @@ func TestSFTPDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -208,9 +218,12 @@ func TestSFTPUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -252,9 +265,12 @@ func TestSFTPDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/splunk/splunk_integration_test.go b/pkg/commands/logging/splunk/splunk_integration_test.go index 563738d96..21852b186 100644 --- a/pkg/commands/logging/splunk/splunk_integration_test.go +++ b/pkg/commands/logging/splunk/splunk_integration_test.go @@ -3,6 +3,7 @@ package splunk_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestSplunkCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestSplunkList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestSplunkDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestSplunkUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestSplunkDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -263,7 +279,7 @@ func createSplunkOK(i *fastly.CreateSplunkInput) (*fastly.Splunk, error) { }, nil } -func createSplunkError(i *fastly.CreateSplunkInput) (*fastly.Splunk, error) { +func createSplunkError(_ *fastly.CreateSplunkInput) (*fastly.Splunk, error) { return nil, errTest } @@ -302,7 +318,7 @@ func listSplunksOK(i *fastly.ListSplunksInput) ([]*fastly.Splunk, error) { }, nil } -func listSplunksError(i *fastly.ListSplunksInput) ([]*fastly.Splunk, error) { +func listSplunksError(_ *fastly.ListSplunksInput) ([]*fastly.Splunk, error) { return nil, errTest } @@ -367,7 +383,7 @@ func getSplunkOK(i *fastly.GetSplunkInput) (*fastly.Splunk, error) { }, nil } -func getSplunkError(i *fastly.GetSplunkInput) (*fastly.Splunk, error) { +func getSplunkError(_ *fastly.GetSplunkInput) (*fastly.Splunk, error) { return nil, errTest } @@ -405,14 +421,14 @@ func updateSplunkOK(i *fastly.UpdateSplunkInput) (*fastly.Splunk, error) { }, nil } -func updateSplunkError(i *fastly.UpdateSplunkInput) (*fastly.Splunk, error) { +func updateSplunkError(_ *fastly.UpdateSplunkInput) (*fastly.Splunk, error) { return nil, errTest } -func deleteSplunkOK(i *fastly.DeleteSplunkInput) error { +func deleteSplunkOK(_ *fastly.DeleteSplunkInput) error { return nil } -func deleteSplunkError(i *fastly.DeleteSplunkInput) error { +func deleteSplunkError(_ *fastly.DeleteSplunkInput) error { return errTest } diff --git a/pkg/commands/logging/sumologic/sumologic_integration_test.go b/pkg/commands/logging/sumologic/sumologic_integration_test.go index e70420e5d..73256ba2b 100644 --- a/pkg/commands/logging/sumologic/sumologic_integration_test.go +++ b/pkg/commands/logging/sumologic/sumologic_integration_test.go @@ -3,6 +3,7 @@ package sumologic_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestSumologicCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestSumologicList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestSumologicDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestSumologicUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestSumologicDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/logging/syslog/syslog_integration_test.go b/pkg/commands/logging/syslog/syslog_integration_test.go index 6447d7f70..9527851fc 100644 --- a/pkg/commands/logging/syslog/syslog_integration_test.go +++ b/pkg/commands/logging/syslog/syslog_integration_test.go @@ -3,6 +3,7 @@ package syslog_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -44,9 +45,12 @@ func TestSyslogCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -114,9 +118,12 @@ func TestSyslogList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -156,9 +163,12 @@ func TestSyslogDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -200,9 +210,12 @@ func TestSyslogUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -244,9 +257,12 @@ func TestSyslogDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/pop/pop_test.go b/pkg/commands/pop/pop_test.go index 15e4ee0b4..416837b08 100644 --- a/pkg/commands/pop/pop_test.go +++ b/pkg/commands/pop/pop_test.go @@ -2,6 +2,7 @@ package pop_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -32,9 +33,12 @@ func TestAllDatacenters(t *testing.T) { }, nil }, } - opts := testutil.NewRunOpts(args, &stdout) - opts.APIClient = mock.APIClient(api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(args, &stdout) + opts.APIClientFactory = mock.APIClient(api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertNoError(t, err) testutil.AssertString(t, "\nNAME CODE GROUP SHIELD COORDINATES\nFoobar FBR Bar Baz {Latitude:1 Longtitude:2 X:3 Y:4}\n", stdout.String()) } diff --git a/pkg/commands/products/products_test.go b/pkg/commands/products/products_test.go index 0d3a7ccbb..472b5ceef 100644 --- a/pkg/commands/products/products_test.go +++ b/pkg/commands/products/products_test.go @@ -2,6 +2,7 @@ package products_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -133,9 +134,12 @@ Web Sockets true testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/profile/profile_test.go b/pkg/commands/profile/profile_test.go index aebba8c59..596a36bcd 100644 --- a/pkg/commands/profile/profile_test.go +++ b/pkg/commands/profile/profile_test.go @@ -114,7 +114,7 @@ func TestCreate(t *testing.T) { ) opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -146,7 +146,10 @@ func TestCreate(t *testing.T) { // Call `app.Run()` and wait for response go func() { - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) done <- true }() @@ -171,7 +174,10 @@ func TestCreate(t *testing.T) { stdin = testcase.Stdin[0] } opts.Stdin = strings.NewReader(stdin) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) } t.Log(stdout.String()) @@ -259,7 +265,7 @@ func TestDelete(t *testing.T) { ) opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -270,7 +276,10 @@ func TestDelete(t *testing.T) { // an in-memory representation of the config file we want to be using. opts.ConfigFile = testcase.ConfigFile - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) t.Log(stdout.String()) @@ -473,7 +482,7 @@ func TestList(t *testing.T) { ) opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -484,7 +493,10 @@ func TestList(t *testing.T) { // an in-memory representation of the config file we want to be using. opts.ConfigFile = testcase.ConfigFile - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) t.Log(stdout.String()) @@ -576,7 +588,7 @@ func TestSwitch(t *testing.T) { ) opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -587,7 +599,10 @@ func TestSwitch(t *testing.T) { // an in-memory representation of the config file we want to be using. opts.ConfigFile = testcase.ConfigFile - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) t.Log(stdout.String()) @@ -721,7 +736,7 @@ func TestToken(t *testing.T) { ) opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -732,7 +747,10 @@ func TestToken(t *testing.T) { // an in-memory representation of the config file we want to be using. opts.ConfigFile = testcase.ConfigFile - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) t.Log(stdout.String()) @@ -833,7 +851,7 @@ func TestUpdate(t *testing.T) { ) opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -863,7 +881,10 @@ func TestUpdate(t *testing.T) { // Call `app.Run()` and wait for response go func() { - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) done <- true }() @@ -888,7 +909,10 @@ func TestUpdate(t *testing.T) { stdin = testcase.Stdin[0] } opts.Stdin = strings.NewReader(stdin) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) } t.Log(stdout.String()) diff --git a/pkg/commands/purge/purge_test.go b/pkg/commands/purge/purge_test.go index 96e7e743b..a0f92969e 100644 --- a/pkg/commands/purge/purge_test.go +++ b/pkg/commands/purge/purge_test.go @@ -2,6 +2,7 @@ package purge_test import ( "bytes" + "io" "reflect" "testing" @@ -53,9 +54,12 @@ func TestPurgeAll(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -104,9 +108,12 @@ func TestPurgeKeys(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) assertKeys(testcase.WantError, testcase.Args, keys, t) @@ -181,9 +188,12 @@ func TestPurgeKey(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) @@ -236,9 +246,12 @@ func TestPurgeURL(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/ratelimit/ratelimit_test.go b/pkg/commands/ratelimit/ratelimit_test.go index 591e1b71d..fc2553c3d 100644 --- a/pkg/commands/ratelimit/ratelimit_test.go +++ b/pkg/commands/ratelimit/ratelimit_test.go @@ -2,6 +2,7 @@ package ratelimit_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -45,9 +46,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -83,9 +87,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -128,9 +135,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -177,9 +187,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -218,9 +231,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/resourcelink/resourcelink_test.go b/pkg/commands/resourcelink/resourcelink_test.go index 6d4577410..9cf23064c 100644 --- a/pkg/commands/resourcelink/resourcelink_test.go +++ b/pkg/commands/resourcelink/resourcelink_test.go @@ -3,15 +3,17 @@ package resourcelink_test import ( "bytes" "fmt" + "io" "strings" "testing" "time" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/resourcelink" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestCreateServiceResourceCommand(t *testing.T) { @@ -144,7 +146,8 @@ func TestCreateServiceResourceCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) + args := testutil.Args(resourcelink.RootName + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.CreateResourceFn var apiInvoked bool @@ -153,9 +156,11 @@ func TestCreateServiceResourceCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) @@ -246,7 +251,8 @@ func TestDeleteServiceResourceCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) + args := testutil.Args(resourcelink.RootName + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.DeleteResourceFn var apiInvoked bool @@ -255,9 +261,11 @@ func TestDeleteServiceResourceCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) @@ -340,7 +348,8 @@ Last edited (UTC): 2023-10-15 12:18`, testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) + args := testutil.Args(resourcelink.RootName + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.GetResourceFn var apiInvoked bool @@ -349,9 +358,11 @@ Last edited (UTC): 2023-10-15 12:18`, return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) @@ -452,7 +463,8 @@ Resource Link 3/3 testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) + args := testutil.Args(resourcelink.RootName + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.ListResourcesFn var apiInvoked bool @@ -461,9 +473,11 @@ Resource Link 3/3 return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, strings.ReplaceAll(strings.TrimSpace(stdout.String()), "\t", " ")) @@ -587,7 +601,8 @@ func TestUpdateServiceResourceCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(resourcelink.RootName+" "+testcase.args), &stdout) + args := testutil.Args(resourcelink.RootName + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.UpdateResourceFn var apiInvoked bool @@ -596,9 +611,11 @@ func TestUpdateServiceResourceCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, strings.TrimSpace(stdout.String())) diff --git a/pkg/commands/secretstore/secretstore_test.go b/pkg/commands/secretstore/secretstore_test.go index 7a0dca56b..96be7878f 100644 --- a/pkg/commands/secretstore/secretstore_test.go +++ b/pkg/commands/secretstore/secretstore_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "testing" "time" @@ -77,7 +78,8 @@ func TestCreateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstore.RootNameStore+" "+testcase.args), &stdout) + args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.CreateSecretStoreFn var apiInvoked bool @@ -86,9 +88,11 @@ func TestCreateStoreCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) @@ -159,7 +163,8 @@ func TestDeleteStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstore.RootNameStore+" "+testcase.args), &stdout) + args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.DeleteSecretStoreFn var apiInvoked bool @@ -168,9 +173,11 @@ func TestDeleteStoreCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) @@ -246,7 +253,8 @@ func TestDescribeStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstore.RootNameStore+" "+testcase.args), &stdout) + args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.GetSecretStoreFn var apiInvoked bool @@ -255,9 +263,11 @@ func TestDescribeStoreCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) @@ -335,7 +345,8 @@ func TestListStoresCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstore.RootNameStore+" "+testcase.args), &stdout) + args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.ListSecretStoresFn var apiInvoked bool @@ -344,9 +355,11 @@ func TestListStoresCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) diff --git a/pkg/commands/secretstoreentry/secretstoreentry_test.go b/pkg/commands/secretstoreentry/secretstoreentry_test.go index cd731c135..c97756bd6 100644 --- a/pkg/commands/secretstoreentry/secretstoreentry_test.go +++ b/pkg/commands/secretstoreentry/secretstoreentry_test.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "errors" "fmt" + "io" "net/http" "os" "path" @@ -227,7 +228,8 @@ func TestCreateSecretCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstoreentry.RootNameSecret+" "+testcase.args), &stdout) + args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) if testcase.stdin != "" { var stdin bytes.Buffer stdin.WriteString(testcase.stdin) @@ -241,14 +243,16 @@ func TestCreateSecretCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - // Tests generate their own signing keys, which won't match // the hardcoded value. Disable the check against the // hardcoded value. t.Setenv("FASTLY_USE_API_SIGNING_KEY", "1") - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) @@ -325,7 +329,8 @@ func TestDeleteSecretCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstoreentry.RootNameSecret+" "+testcase.args), &stdout) + args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.DeleteSecretFn var apiInvoked bool @@ -334,9 +339,11 @@ func TestDeleteSecretCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) @@ -429,7 +436,8 @@ func TestDescribeSecretCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstoreentry.RootNameSecret+" "+testcase.args), &stdout) + args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.GetSecretFn var apiInvoked bool @@ -438,9 +446,11 @@ func TestDescribeSecretCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) @@ -514,7 +524,8 @@ func TestListSecretsCommand(t *testing.T) { testcase := testcase t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testutil.Args(secretstoreentry.RootNameSecret+" "+testcase.args), &stdout) + args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) + opts := testutil.NewRunOpts(args, &stdout) f := testcase.api.ListSecretsFn var apiInvoked bool @@ -523,9 +534,11 @@ func TestListSecretsCommand(t *testing.T) { return f(i) } - opts.APIClient = mock.APIClient(testcase.api) - - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) diff --git a/pkg/commands/service/service_test.go b/pkg/commands/service/service_test.go index 4e028dec3..585d8a8ee 100644 --- a/pkg/commands/service/service_test.go +++ b/pkg/commands/service/service_test.go @@ -3,6 +3,7 @@ package service_test import ( "bytes" "errors" + "io" "os" "path/filepath" "regexp" @@ -60,9 +61,12 @@ func TestServiceCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -133,9 +137,12 @@ func TestServiceList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -190,9 +197,12 @@ func TestServiceDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -231,9 +241,12 @@ func TestServiceSearch(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -296,9 +309,12 @@ func TestServiceUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -384,9 +400,12 @@ func TestServiceDelete(t *testing.T) { }() var stdout bytes.Buffer - runOpts := testutil.NewRunOpts(testcase.args, &stdout) - runOpts.APIClient = mock.APIClient(testcase.api) - runErr := app.Run(runOpts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + runOpts := testutil.NewRunOpts(testcase.args, &stdout) + runOpts.APIClientFactory = mock.APIClient(testcase.api) + return runOpts, nil + } + runErr := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, runErr, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) diff --git a/pkg/commands/serviceauth/service_test.go b/pkg/commands/serviceauth/service_test.go index 6f5f8a215..eb5cbcd94 100644 --- a/pkg/commands/serviceauth/service_test.go +++ b/pkg/commands/serviceauth/service_test.go @@ -3,6 +3,7 @@ package serviceauth_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -40,9 +41,12 @@ func TestServiceAuthCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -106,9 +110,12 @@ func TestServiceAuthList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) @@ -164,9 +171,12 @@ func TestServiceAuthDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) @@ -205,9 +215,12 @@ func TestServiceAuthUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -241,9 +254,12 @@ func TestServiceAuthDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/serviceversion/serviceversion_test.go b/pkg/commands/serviceversion/serviceversion_test.go index d2a8b7044..2d5976dcd 100644 --- a/pkg/commands/serviceversion/serviceversion_test.go +++ b/pkg/commands/serviceversion/serviceversion_test.go @@ -2,6 +2,7 @@ package serviceversion_test import ( "bytes" + "io" "strings" "testing" @@ -49,9 +50,12 @@ func TestVersionClone(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -101,9 +105,12 @@ func TestVersionList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertString(t, testcase.wantOutput, stdout.String()) }) @@ -149,9 +156,12 @@ func TestVersionUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -201,9 +211,12 @@ func TestVersionActivate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -251,9 +264,12 @@ func TestVersionDeactivate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) @@ -293,9 +309,12 @@ func TestVersionLock(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index 364ca0351..57f6049ef 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -227,16 +227,17 @@ func TestSSO(t *testing.T) { t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) + opts.APIClientFactory = mock.APIClient(testcase.API) if testcase.HTTPClient != nil { opts.HTTPClient = testcase.HTTPClient } - if testcase.ConfigFile != nil { opts.ConfigFile = *testcase.ConfigFile } - + if testcase.Opener != nil { + opts.Opener = testcase.Opener + } if testcase.AuthResult != nil { result := make(chan auth.AuthorizationResult) opts.AuthServer = testutil.MockAuthServer{ @@ -247,10 +248,6 @@ func TestSSO(t *testing.T) { }() } - if testcase.Opener != nil { - opts.Opener = testcase.Opener - } - var err error if len(testcase.Stdin) > 1 { @@ -272,7 +269,10 @@ func TestSSO(t *testing.T) { // Call `app.Run()` and wait for response go func() { - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) done <- true }() @@ -297,7 +297,10 @@ func TestSSO(t *testing.T) { stdin = testcase.Stdin[0] } opts.Stdin = strings.NewReader(stdin) - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(testcase.Args, nil) } if testcase.ExpectedConfigProfile != nil { diff --git a/pkg/commands/stats/historical_test.go b/pkg/commands/stats/historical_test.go index 9717fd5d5..ba5d65480 100644 --- a/pkg/commands/stats/historical_test.go +++ b/pkg/commands/stats/historical_test.go @@ -3,6 +3,7 @@ package stats_test import ( "bytes" "encoding/json" + "io" "strings" "testing" @@ -41,9 +42,12 @@ func TestHistorical(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/stats/regions_test.go b/pkg/commands/stats/regions_test.go index cac75754f..66fadb5ff 100644 --- a/pkg/commands/stats/regions_test.go +++ b/pkg/commands/stats/regions_test.go @@ -3,13 +3,15 @@ package stats_test import ( "bytes" "errors" + "io" "strings" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) func TestRegions(t *testing.T) { @@ -35,9 +37,12 @@ func TestRegions(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.APIClient = mock.APIClient(testcase.api) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.api) + return opts, nil + } + err := app.Run(testcase.args, nil) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) }) diff --git a/pkg/commands/tls/config/config_test.go b/pkg/commands/tls/config/config_test.go index 283e40f6f..f522a8ee5 100644 --- a/pkg/commands/tls/config/config_test.go +++ b/pkg/commands/tls/config/config_test.go @@ -3,12 +3,14 @@ package config_test import ( "bytes" "fmt" + "io" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -68,9 +70,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -125,9 +130,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -175,9 +183,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/tls/custom/activation/activation_test.go b/pkg/commands/tls/custom/activation/activation_test.go index 9e8885972..c370e35d0 100644 --- a/pkg/commands/tls/custom/activation/activation_test.go +++ b/pkg/commands/tls/custom/activation/activation_test.go @@ -3,12 +3,14 @@ package activation_test import ( "bytes" "fmt" + "io" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -63,9 +65,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -106,9 +111,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -153,9 +161,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -197,9 +208,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -250,9 +264,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/tls/custom/certificate/certificate_test.go b/pkg/commands/tls/custom/certificate/certificate_test.go index 3fc468b26..88578036d 100644 --- a/pkg/commands/tls/custom/certificate/certificate_test.go +++ b/pkg/commands/tls/custom/certificate/certificate_test.go @@ -3,6 +3,7 @@ package certificate_test import ( "bytes" "fmt" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -56,9 +57,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -99,9 +103,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -153,9 +160,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -204,9 +214,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -267,9 +280,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/tls/custom/domain/domain_test.go b/pkg/commands/tls/custom/domain/domain_test.go index 6d289ba4c..280442192 100644 --- a/pkg/commands/tls/custom/domain/domain_test.go +++ b/pkg/commands/tls/custom/domain/domain_test.go @@ -2,12 +2,14 @@ package domain_test import ( "bytes" + "io" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -50,9 +52,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/tls/custom/privatekey/privatekey_test.go b/pkg/commands/tls/custom/privatekey/privatekey_test.go index 640fb34e8..84835b84d 100644 --- a/pkg/commands/tls/custom/privatekey/privatekey_test.go +++ b/pkg/commands/tls/custom/privatekey/privatekey_test.go @@ -2,12 +2,14 @@ package privatekey_test import ( "bytes" + "io" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -61,9 +63,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -104,9 +109,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -155,9 +163,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -203,9 +214,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/tls/platform/platform_test.go b/pkg/commands/tls/platform/platform_test.go index b26042270..72219eed5 100644 --- a/pkg/commands/tls/platform/platform_test.go +++ b/pkg/commands/tls/platform/platform_test.go @@ -3,12 +3,14 @@ package platform_test import ( "bytes" "fmt" + "io" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -59,9 +61,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -102,9 +107,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -151,9 +159,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -197,9 +208,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -252,9 +266,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/tls/subscription/subscription_test.go b/pkg/commands/tls/subscription/subscription_test.go index 48f0f0744..8ae798002 100644 --- a/pkg/commands/tls/subscription/subscription_test.go +++ b/pkg/commands/tls/subscription/subscription_test.go @@ -3,12 +3,14 @@ package subscription_test import ( "bytes" "fmt" + "io" "testing" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" - "github.com/fastly/go-fastly/v8/fastly" ) const ( @@ -59,9 +61,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -102,9 +107,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -152,9 +160,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -199,9 +210,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -248,9 +262,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/user/user_test.go b/pkg/commands/user/user_test.go index da5d9fe48..0b3efcf36 100644 --- a/pkg/commands/user/user_test.go +++ b/pkg/commands/user/user_test.go @@ -3,6 +3,7 @@ package user_test import ( "bytes" "fmt" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -44,9 +45,12 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -87,9 +91,12 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -146,9 +153,12 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -195,9 +205,12 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -277,9 +290,12 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) diff --git a/pkg/commands/vcl/condition/condition_test.go b/pkg/commands/vcl/condition/condition_test.go index 431746d93..0f7d34098 100644 --- a/pkg/commands/vcl/condition/condition_test.go +++ b/pkg/commands/vcl/condition/condition_test.go @@ -3,6 +3,7 @@ package condition_test import ( "bytes" "errors" + "io" "strings" "testing" @@ -43,9 +44,12 @@ func TestConditionCreate(t *testing.T) { for _, testcase := range scenarios { t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -82,9 +86,12 @@ func TestConditionDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -130,9 +137,12 @@ func TestConditionUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -167,9 +177,12 @@ func TestConditionDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) @@ -232,9 +245,12 @@ func TestConditionList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertString(t, testcase.WantOutput, stdout.String()) }) diff --git a/pkg/commands/vcl/custom/custom_test.go b/pkg/commands/vcl/custom/custom_test.go index fb7d84bcf..7a14df390 100644 --- a/pkg/commands/vcl/custom/custom_test.go +++ b/pkg/commands/vcl/custom/custom_test.go @@ -2,6 +2,7 @@ package custom_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -158,9 +159,12 @@ func TestVCLCustomCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) testutil.AssertPathContentFlag("content", testcase.WantError, testcase.Args, "example.vcl", content, t) @@ -234,9 +238,12 @@ func TestVCLCustomDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -296,9 +303,12 @@ func TestVCLCustomDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -362,9 +372,12 @@ func TestVCLCustomList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -473,9 +486,12 @@ func TestVCLCustomUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) testutil.AssertPathContentFlag("content", testcase.WantError, testcase.Args, "example.vcl", content, t) diff --git a/pkg/commands/vcl/snippet/snippet_test.go b/pkg/commands/vcl/snippet/snippet_test.go index b19fc3787..6edc2f22d 100644 --- a/pkg/commands/vcl/snippet/snippet_test.go +++ b/pkg/commands/vcl/snippet/snippet_test.go @@ -2,6 +2,7 @@ package snippet_test import ( "bytes" + "io" "testing" "github.com/fastly/go-fastly/v8/fastly" @@ -192,9 +193,12 @@ func TestVCLSnippetCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, testutil.StripNewLines(stdout.String()), testcase.WantOutput) @@ -269,9 +273,12 @@ func TestVCLSnippetDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -351,9 +358,12 @@ func TestVCLSnippetDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -417,9 +427,12 @@ func TestVCLSnippetList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) }) @@ -558,9 +571,12 @@ func TestVCLSnippetUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) - opts.APIClient = mock.APIClient(testcase.API) - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts.APIClientFactory = mock.APIClient(testcase.API) + return opts, nil + } + err := app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) testutil.AssertStringContains(t, testutil.StripNewLines(stdout.String()), testcase.WantOutput) testutil.AssertPathContentFlag("content", testcase.WantError, testcase.Args, "snippet.vcl", content, t) diff --git a/pkg/commands/version/version_test.go b/pkg/commands/version/version_test.go index 59339b5ec..6ad86623d 100644 --- a/pkg/commands/version/version_test.go +++ b/pkg/commands/version/version_test.go @@ -3,6 +3,7 @@ package version_test import ( "bytes" "fmt" + "io" "os" "path/filepath" "runtime" @@ -74,7 +75,10 @@ func TestVersion(t *testing.T) { Binary: "viceroy", }), } - err = app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err = app.Run(args, nil) t.Log(stdout.String()) diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index 9c2bf1e7f..ed10c451f 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "net/http" "net/http/httptest" "strings" @@ -75,7 +76,10 @@ func TestWhoami(t *testing.T) { opts := testutil.NewRunOpts(testcase.args, &stdout) opts.Env = testcase.env opts.HTTPClient = testcase.client - err := app.Run(opts) + app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + return opts, nil + } + err := app.Run(testcase.args, nil) opts.ConfigFile = config.File{} t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.wantError) diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index 6c810fb0a..14979c72a 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -104,9 +104,9 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { } return app.RunOpts{ - Args: args, - APIClient: mock.APIClient(mock.API{}), - AuthServer: &MockAuthServer{}, + Args: args, + APIClientFactory: mock.APIClient(mock.API{}), + AuthServer: &MockAuthServer{}, ConfigFile: config.File{ Profiles: TokenProfile(), }, From 6ef734834a745d63d3d6cf16406def539f2d61ec Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 13 Nov 2023 10:52:22 +0000 Subject: [PATCH 103/115] refactor: correct some linter items --- pkg/app/run.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 4b7f8eab0..06a624e77 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -35,6 +35,7 @@ import ( "github.com/fastly/cli/pkg/text" ) +// Run kick starts the CLI application. func Run(args []string, stdin io.Reader) error { opts, err := Init(args, stdin) if err != nil { @@ -382,8 +383,9 @@ func processToken(commands []cmd.Command, commandName string, m *manifest.Data, if g.Verbose() { displayToken(tokenSource, m.File.Profile, *g, out) } - - checkConfigPermissions(g.Flags.Quiet, commandName, tokenSource, out) + if !g.Flags.Quiet { + checkConfigPermissions(commandName, tokenSource, out) + } return token, nil } @@ -612,16 +614,14 @@ func displayToken(tokenSource lookup.Source, profileName string, g global.Data, // If we are using the token from config file, check the file's permissions // to assert if they are not too open or have been altered outside of the // application and warn if so. -func checkConfigPermissions(quietMode bool, commandName string, tokenSource lookup.Source, out io.Writer) { +func checkConfigPermissions(commandName string, tokenSource lookup.Source, out io.Writer) { segs := strings.Split(commandName, " ") if tokenSource == lookup.SourceFile && (len(segs) > 0 && segs[0] != "profile") { if fi, err := os.Stat(config.FilePath); err == nil { if mode := fi.Mode().Perm(); mode > config.FilePermissions { - if !quietMode { - text.Warning(out, "Unprotected configuration file.\n\n") - text.Output(out, "Permissions for '%s' are too open\n\n", config.FilePath) - text.Output(out, "It is recommended that your configuration file is NOT accessible by others.\n\n") - } + text.Warning(out, "Unprotected configuration file.\n\n") + text.Output(out, "Permissions for '%s' are too open\n\n", config.FilePath) + text.Output(out, "It is recommended that your configuration file is NOT accessible by others.\n\n") } } } From 253abe54a02ba16b8d12c9a45d9fe5e91881e48c Mon Sep 17 00:00:00 2001 From: Integralist Date: Mon, 13 Nov 2023 20:42:44 +0000 Subject: [PATCH 104/115] refactor: all the things --- pkg/app/run.go | 271 +++---- pkg/app/run_test.go | 5 +- pkg/app/usage.go | 46 +- pkg/auth/auth.go | 51 +- pkg/commands/acl/acl_test.go | 21 +- pkg/commands/acl/create.go | 9 +- pkg/commands/acl/delete.go | 9 +- pkg/commands/acl/describe.go | 12 +- pkg/commands/acl/list.go | 12 +- pkg/commands/acl/update.go | 12 +- pkg/commands/aclentry/aclentry_test.go | 21 +- pkg/commands/aclentry/create.go | 12 +- pkg/commands/aclentry/delete.go | 9 +- pkg/commands/aclentry/describe.go | 12 +- pkg/commands/aclentry/list.go | 12 +- pkg/commands/aclentry/update.go | 9 +- pkg/commands/authtoken/authtoken_test.go | 17 +- pkg/commands/authtoken/create.go | 5 +- pkg/commands/authtoken/delete.go | 11 +- pkg/commands/authtoken/describe.go | 6 +- pkg/commands/authtoken/list.go | 5 +- pkg/commands/backend/backend_test.go | 21 +- pkg/commands/backend/create.go | 9 +- pkg/commands/backend/delete.go | 12 +- pkg/commands/backend/describe.go | 12 +- pkg/commands/backend/list.go | 12 +- pkg/commands/backend/update.go | 9 +- pkg/{app => commands}/commands.go | 744 +++++++++--------- pkg/commands/compute/build.go | 7 +- pkg/commands/compute/build_test.go | 39 +- pkg/commands/compute/compute_test.go | 18 +- pkg/commands/compute/deploy.go | 4 +- pkg/commands/compute/deploy_test.go | 11 +- pkg/commands/compute/init.go | 14 +- pkg/commands/compute/init_test.go | 9 +- pkg/commands/compute/metadata_test.go | 21 +- pkg/commands/compute/pack.go | 7 +- pkg/commands/compute/pack_test.go | 5 +- pkg/commands/compute/serve.go | 6 +- pkg/commands/compute/update.go | 10 +- pkg/commands/compute/update_test.go | 5 +- pkg/commands/compute/validate_test.go | 5 +- pkg/commands/config/config_test.go | 5 +- pkg/commands/configstore/configstore_test.go | 25 +- pkg/commands/configstore/create.go | 8 +- pkg/commands/configstore/delete.go | 8 +- pkg/commands/configstore/describe.go | 9 +- pkg/commands/configstore/list.go | 6 +- pkg/commands/configstore/list_services.go | 11 +- pkg/commands/configstore/update.go | 8 +- .../configstoreentry/configstoreentry_test.go | 21 +- pkg/commands/configstoreentry/create.go | 10 +- pkg/commands/configstoreentry/delete.go | 5 +- pkg/commands/configstoreentry/describe.go | 11 +- pkg/commands/configstoreentry/list.go | 11 +- pkg/commands/configstoreentry/update.go | 10 +- pkg/commands/dictionary/create.go | 9 +- pkg/commands/dictionary/delete.go | 9 +- pkg/commands/dictionary/describe.go | 12 +- pkg/commands/dictionary/dictionary_test.go | 21 +- pkg/commands/dictionary/list.go | 9 +- pkg/commands/dictionary/update.go | 10 +- pkg/commands/dictionaryentry/create.go | 9 +- pkg/commands/dictionaryentry/delete.go | 9 +- pkg/commands/dictionaryentry/describe.go | 12 +- .../dictionaryentry/dictionaryitem_test.go | 21 +- pkg/commands/dictionaryentry/list.go | 12 +- pkg/commands/dictionaryentry/update.go | 9 +- pkg/commands/doc.go | 2 + pkg/commands/domain/create.go | 12 +- pkg/commands/domain/delete.go | 12 +- pkg/commands/domain/describe.go | 12 +- pkg/commands/domain/domain_test.go | 25 +- pkg/commands/domain/list.go | 12 +- pkg/commands/domain/update.go | 12 +- pkg/commands/domain/validate.go | 9 +- pkg/commands/healthcheck/create.go | 12 +- pkg/commands/healthcheck/delete.go | 12 +- pkg/commands/healthcheck/describe.go | 12 +- pkg/commands/healthcheck/healthcheck_test.go | 21 +- pkg/commands/healthcheck/list.go | 12 +- pkg/commands/healthcheck/update.go | 12 +- pkg/commands/ip/ip_test.go | 5 +- pkg/commands/kvstore/create.go | 7 +- pkg/commands/kvstore/delete.go | 7 +- pkg/commands/kvstore/describe.go | 7 +- pkg/commands/kvstore/kvstore_test.go | 17 +- pkg/commands/kvstore/list.go | 6 +- pkg/commands/kvstoreentry/create.go | 5 +- pkg/commands/kvstoreentry/delete.go | 5 +- pkg/commands/kvstoreentry/describe.go | 7 +- .../kvstoreentry/kvstoreentry_test.go | 19 +- pkg/commands/kvstoreentry/list.go | 5 +- .../azureblob/azureblob_integration_test.go | 21 +- pkg/commands/logging/azureblob/create.go | 10 +- pkg/commands/logging/azureblob/delete.go | 12 +- pkg/commands/logging/azureblob/describe.go | 12 +- pkg/commands/logging/azureblob/list.go | 12 +- pkg/commands/logging/azureblob/update.go | 10 +- .../bigquery/bigquery_integration_test.go | 21 +- pkg/commands/logging/bigquery/create.go | 7 +- pkg/commands/logging/bigquery/delete.go | 12 +- pkg/commands/logging/bigquery/describe.go | 12 +- pkg/commands/logging/bigquery/list.go | 12 +- pkg/commands/logging/bigquery/update.go | 7 +- .../cloudfiles/cloudfiles_integration_test.go | 21 +- pkg/commands/logging/cloudfiles/create.go | 7 +- pkg/commands/logging/cloudfiles/delete.go | 12 +- pkg/commands/logging/cloudfiles/describe.go | 12 +- pkg/commands/logging/cloudfiles/list.go | 12 +- pkg/commands/logging/cloudfiles/update.go | 7 +- pkg/commands/logging/datadog/create.go | 10 +- .../datadog/datadog_integration_test.go | 21 +- pkg/commands/logging/datadog/delete.go | 12 +- pkg/commands/logging/datadog/describe.go | 12 +- pkg/commands/logging/datadog/list.go | 12 +- pkg/commands/logging/datadog/update.go | 10 +- pkg/commands/logging/digitalocean/create.go | 10 +- pkg/commands/logging/digitalocean/delete.go | 12 +- pkg/commands/logging/digitalocean/describe.go | 12 +- .../digitalocean_integration_test.go | 21 +- pkg/commands/logging/digitalocean/list.go | 12 +- pkg/commands/logging/digitalocean/update.go | 10 +- pkg/commands/logging/elasticsearch/create.go | 10 +- pkg/commands/logging/elasticsearch/delete.go | 12 +- .../logging/elasticsearch/describe.go | 12 +- .../elasticsearch_integration_test.go | 21 +- pkg/commands/logging/elasticsearch/list.go | 12 +- pkg/commands/logging/elasticsearch/update.go | 10 +- pkg/commands/logging/ftp/create.go | 10 +- pkg/commands/logging/ftp/delete.go | 12 +- pkg/commands/logging/ftp/describe.go | 12 +- .../logging/ftp/ftp_integration_test.go | 21 +- pkg/commands/logging/ftp/list.go | 12 +- pkg/commands/logging/ftp/update.go | 10 +- pkg/commands/logging/gcs/create.go | 10 +- pkg/commands/logging/gcs/delete.go | 12 +- pkg/commands/logging/gcs/describe.go | 12 +- .../logging/gcs/gcs_integration_test.go | 21 +- pkg/commands/logging/gcs/list.go | 12 +- pkg/commands/logging/gcs/update.go | 10 +- pkg/commands/logging/googlepubsub/create.go | 10 +- pkg/commands/logging/googlepubsub/delete.go | 12 +- pkg/commands/logging/googlepubsub/describe.go | 12 +- .../googlepubsub_integration_test.go | 21 +- pkg/commands/logging/googlepubsub/list.go | 12 +- pkg/commands/logging/googlepubsub/update.go | 10 +- pkg/commands/logging/heroku/create.go | 10 +- pkg/commands/logging/heroku/delete.go | 12 +- pkg/commands/logging/heroku/describe.go | 12 +- .../logging/heroku/heroku_integration_test.go | 21 +- pkg/commands/logging/heroku/list.go | 12 +- pkg/commands/logging/heroku/update.go | 10 +- pkg/commands/logging/honeycomb/create.go | 10 +- pkg/commands/logging/honeycomb/delete.go | 12 +- pkg/commands/logging/honeycomb/describe.go | 12 +- .../honeycomb/honeycomb_integration_test.go | 21 +- pkg/commands/logging/honeycomb/list.go | 12 +- pkg/commands/logging/honeycomb/update.go | 10 +- pkg/commands/logging/https/create.go | 10 +- pkg/commands/logging/https/delete.go | 12 +- pkg/commands/logging/https/describe.go | 12 +- .../logging/https/https_integration_test.go | 21 +- pkg/commands/logging/https/list.go | 12 +- pkg/commands/logging/https/update.go | 10 +- pkg/commands/logging/kafka/create.go | 10 +- pkg/commands/logging/kafka/delete.go | 12 +- pkg/commands/logging/kafka/describe.go | 12 +- .../logging/kafka/kafka_integration_test.go | 47 +- pkg/commands/logging/kafka/list.go | 12 +- pkg/commands/logging/kafka/update.go | 10 +- pkg/commands/logging/kinesis/create.go | 7 +- pkg/commands/logging/kinesis/delete.go | 12 +- pkg/commands/logging/kinesis/describe.go | 12 +- .../kinesis/kinesis_integration_test.go | 21 +- pkg/commands/logging/kinesis/list.go | 12 +- pkg/commands/logging/kinesis/update.go | 10 +- pkg/commands/logging/loggly/create.go | 10 +- pkg/commands/logging/loggly/delete.go | 12 +- pkg/commands/logging/loggly/describe.go | 12 +- pkg/commands/logging/loggly/list.go | 12 +- .../logging/loggly/loggly_integration_test.go | 21 +- pkg/commands/logging/loggly/update.go | 10 +- pkg/commands/logging/logshuttle/create.go | 10 +- pkg/commands/logging/logshuttle/delete.go | 12 +- pkg/commands/logging/logshuttle/describe.go | 12 +- pkg/commands/logging/logshuttle/list.go | 12 +- .../logshuttle/logshuttle_integration_test.go | 21 +- pkg/commands/logging/logshuttle/update.go | 10 +- pkg/commands/logging/newrelic/create.go | 12 +- pkg/commands/logging/newrelic/delete.go | 12 +- pkg/commands/logging/newrelic/describe.go | 12 +- pkg/commands/logging/newrelic/list.go | 12 +- .../logging/newrelic/newrelic_test.go | 21 +- pkg/commands/logging/newrelic/update.go | 12 +- pkg/commands/logging/newrelicotlp/create.go | 12 +- pkg/commands/logging/newrelicotlp/delete.go | 12 +- pkg/commands/logging/newrelicotlp/describe.go | 12 +- pkg/commands/logging/newrelicotlp/list.go | 12 +- .../logging/newrelicotlp/newrelicotlp_test.go | 21 +- pkg/commands/logging/newrelicotlp/update.go | 12 +- pkg/commands/logging/openstack/create.go | 10 +- pkg/commands/logging/openstack/delete.go | 12 +- pkg/commands/logging/openstack/describe.go | 12 +- pkg/commands/logging/openstack/list.go | 12 +- .../openstack/openstack_integration_test.go | 21 +- pkg/commands/logging/openstack/update.go | 10 +- pkg/commands/logging/papertrail/create.go | 10 +- pkg/commands/logging/papertrail/delete.go | 12 +- pkg/commands/logging/papertrail/describe.go | 12 +- pkg/commands/logging/papertrail/list.go | 12 +- .../papertrail/papertrail_integration_test.go | 21 +- pkg/commands/logging/papertrail/update.go | 10 +- pkg/commands/logging/s3/create.go | 7 +- pkg/commands/logging/s3/delete.go | 12 +- pkg/commands/logging/s3/describe.go | 12 +- pkg/commands/logging/s3/list.go | 12 +- .../logging/s3/s3_integration_test.go | 21 +- pkg/commands/logging/s3/update.go | 10 +- pkg/commands/logging/scalyr/create.go | 10 +- pkg/commands/logging/scalyr/delete.go | 12 +- pkg/commands/logging/scalyr/describe.go | 12 +- pkg/commands/logging/scalyr/list.go | 12 +- .../logging/scalyr/scalyr_integration_test.go | 21 +- pkg/commands/logging/scalyr/update.go | 10 +- pkg/commands/logging/sftp/create.go | 10 +- pkg/commands/logging/sftp/delete.go | 12 +- pkg/commands/logging/sftp/describe.go | 12 +- pkg/commands/logging/sftp/list.go | 12 +- .../logging/sftp/sftp_integration_test.go | 21 +- pkg/commands/logging/sftp/update.go | 10 +- pkg/commands/logging/splunk/create.go | 10 +- pkg/commands/logging/splunk/delete.go | 12 +- pkg/commands/logging/splunk/describe.go | 12 +- pkg/commands/logging/splunk/list.go | 12 +- .../logging/splunk/splunk_integration_test.go | 21 +- pkg/commands/logging/splunk/update.go | 10 +- pkg/commands/logging/sumologic/create.go | 10 +- pkg/commands/logging/sumologic/delete.go | 12 +- pkg/commands/logging/sumologic/describe.go | 12 +- pkg/commands/logging/sumologic/list.go | 12 +- .../sumologic/sumologic_integration_test.go | 21 +- pkg/commands/logging/sumologic/update.go | 10 +- pkg/commands/logging/syslog/create.go | 10 +- pkg/commands/logging/syslog/delete.go | 12 +- pkg/commands/logging/syslog/describe.go | 12 +- pkg/commands/logging/syslog/list.go | 12 +- .../logging/syslog/syslog_integration_test.go | 29 +- pkg/commands/logging/syslog/update.go | 10 +- pkg/commands/logtail/root.go | 9 +- pkg/commands/pop/pop_test.go | 5 +- pkg/commands/products/products_test.go | 5 +- pkg/commands/products/root.go | 9 +- pkg/commands/profile/create.go | 6 +- pkg/commands/profile/profile_test.go | 49 +- pkg/commands/profile/update.go | 6 +- pkg/commands/purge/purge_test.go | 17 +- pkg/commands/purge/root.go | 8 +- pkg/commands/ratelimit/create.go | 9 +- pkg/commands/ratelimit/delete.go | 7 +- pkg/commands/ratelimit/describe.go | 7 +- pkg/commands/ratelimit/list.go | 9 +- pkg/commands/ratelimit/ratelimit_test.go | 21 +- pkg/commands/ratelimit/update.go | 5 +- pkg/commands/resourcelink/create.go | 14 +- pkg/commands/resourcelink/delete.go | 14 +- pkg/commands/resourcelink/describe.go | 12 +- pkg/commands/resourcelink/list.go | 9 +- .../resourcelink/resourcelink_test.go | 21 +- pkg/commands/resourcelink/update.go | 14 +- pkg/commands/secretstore/create.go | 7 +- pkg/commands/secretstore/delete.go | 7 +- pkg/commands/secretstore/describe.go | 10 +- pkg/commands/secretstore/list.go | 7 +- pkg/commands/secretstore/secretstore_test.go | 17 +- pkg/commands/secretstoreentry/create.go | 5 +- pkg/commands/secretstoreentry/delete.go | 7 +- pkg/commands/secretstoreentry/describe.go | 10 +- pkg/commands/secretstoreentry/list.go | 7 +- .../secretstoreentry/secretstoreentry_test.go | 19 +- pkg/commands/service/create.go | 3 +- pkg/commands/service/delete.go | 17 +- pkg/commands/service/describe.go | 11 +- pkg/commands/service/search.go | 10 +- pkg/commands/service/service_test.go | 25 +- pkg/commands/service/update.go | 12 +- pkg/commands/serviceauth/create.go | 11 +- pkg/commands/serviceauth/delete.go | 10 +- pkg/commands/serviceauth/describe.go | 10 +- pkg/commands/serviceauth/service_test.go | 21 +- pkg/commands/serviceauth/update.go | 10 +- pkg/commands/serviceversion/activate.go | 12 +- pkg/commands/serviceversion/clone.go | 12 +- pkg/commands/serviceversion/deactivate.go | 12 +- pkg/commands/serviceversion/list.go | 12 +- pkg/commands/serviceversion/lock.go | 12 +- .../serviceversion/serviceversion_test.go | 25 +- pkg/commands/serviceversion/update.go | 9 +- pkg/commands/sso/root.go | 28 +- pkg/commands/sso/sso_test.go | 15 +- pkg/commands/stats/historical.go | 9 +- pkg/commands/stats/historical_test.go | 5 +- pkg/commands/stats/realtime.go | 12 +- pkg/commands/stats/regions_test.go | 5 +- pkg/commands/tls/config/config_test.go | 13 +- pkg/commands/tls/config/describe.go | 12 +- pkg/commands/tls/config/list.go | 8 +- pkg/commands/tls/config/update.go | 12 +- .../tls/custom/activation/activation_test.go | 21 +- pkg/commands/tls/custom/activation/create.go | 12 +- pkg/commands/tls/custom/activation/delete.go | 10 +- .../tls/custom/activation/describe.go | 12 +- pkg/commands/tls/custom/activation/list.go | 8 +- pkg/commands/tls/custom/activation/update.go | 12 +- .../custom/certificate/certificate_test.go | 21 +- pkg/commands/tls/custom/certificate/create.go | 8 +- pkg/commands/tls/custom/certificate/delete.go | 10 +- .../tls/custom/certificate/describe.go | 10 +- pkg/commands/tls/custom/certificate/list.go | 8 +- pkg/commands/tls/custom/certificate/update.go | 8 +- pkg/commands/tls/custom/domain/domain_test.go | 5 +- pkg/commands/tls/custom/domain/list.go | 8 +- pkg/commands/tls/custom/privatekey/create.go | 12 +- pkg/commands/tls/custom/privatekey/delete.go | 10 +- .../tls/custom/privatekey/describe.go | 10 +- pkg/commands/tls/custom/privatekey/list.go | 8 +- .../tls/custom/privatekey/privatekey_test.go | 17 +- pkg/commands/tls/platform/create.go | 8 +- pkg/commands/tls/platform/delete.go | 10 +- pkg/commands/tls/platform/describe.go | 10 +- pkg/commands/tls/platform/list.go | 8 +- pkg/commands/tls/platform/platform_test.go | 21 +- pkg/commands/tls/platform/update.go | 10 +- pkg/commands/tls/subscription/create.go | 8 +- pkg/commands/tls/subscription/delete.go | 12 +- pkg/commands/tls/subscription/describe.go | 12 +- pkg/commands/tls/subscription/list.go | 8 +- .../tls/subscription/subscription_test.go | 21 +- pkg/commands/tls/subscription/update.go | 8 +- pkg/commands/update/root.go | 11 +- pkg/commands/user/create.go | 5 +- pkg/commands/user/delete.go | 7 +- pkg/commands/user/describe.go | 9 +- pkg/commands/user/list.go | 5 +- pkg/commands/user/update.go | 15 +- pkg/commands/user/user_test.go | 21 +- pkg/commands/vcl/condition/condition_test.go | 21 +- pkg/commands/vcl/condition/create.go | 9 +- pkg/commands/vcl/condition/delete.go | 12 +- pkg/commands/vcl/condition/describe.go | 9 +- pkg/commands/vcl/condition/list.go | 12 +- pkg/commands/vcl/condition/update.go | 11 +- pkg/commands/vcl/custom/create.go | 12 +- pkg/commands/vcl/custom/custom_test.go | 21 +- pkg/commands/vcl/custom/delete.go | 12 +- pkg/commands/vcl/custom/describe.go | 12 +- pkg/commands/vcl/custom/list.go | 12 +- pkg/commands/vcl/custom/update.go | 9 +- pkg/commands/vcl/snippet/create.go | 12 +- pkg/commands/vcl/snippet/delete.go | 12 +- pkg/commands/vcl/snippet/describe.go | 9 +- pkg/commands/vcl/snippet/list.go | 12 +- pkg/commands/vcl/snippet/snippet_test.go | 21 +- pkg/commands/vcl/snippet/update.go | 9 +- pkg/commands/version/root.go | 13 +- pkg/commands/version/version_test.go | 7 +- pkg/commands/whoami/whoami_test.go | 7 +- pkg/global/doc.go | 3 +- pkg/global/global.go | 57 +- pkg/testutil/args.go | 29 +- 370 files changed, 2537 insertions(+), 3060 deletions(-) rename pkg/{app => commands}/commands.go (75%) create mode 100644 pkg/commands/doc.go diff --git a/pkg/app/run.go b/pkg/app/run.go index 06a624e77..d86b6df54 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -14,11 +14,13 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/kingpin" "github.com/fatih/color" + "github.com/hashicorp/cap/oidc" "github.com/skratchdot/open-golang/open" "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" + "github.com/fastly/cli/pkg/commands" "github.com/fastly/cli/pkg/commands/compute" "github.com/fastly/cli/pkg/commands/update" "github.com/fastly/cli/pkg/commands/version" @@ -37,17 +39,17 @@ import ( // Run kick starts the CLI application. func Run(args []string, stdin io.Reader) error { - opts, err := Init(args, stdin) + data, err := Init(args, stdin) if err != nil { return fmt.Errorf("failed to initialise application: %w", err) } - return Exec(opts) + return Exec(data) } // Init constructs all the required objects and data for Exec(). // // NOTE: We define as a package level variable so we can mock output for tests. -var Init = func(args []string, stdin io.Reader) (RunOpts, error) { +var Init = func(args []string, stdin io.Reader) (*global.Data, error) { // Parse the arguments provided by the user via the command-line interface. args = args[1:] @@ -91,7 +93,7 @@ var Init = func(args []string, stdin io.Reader) (RunOpts, error) { cfg.SetAutoYes(autoYes) cfg.SetNonInteractive(nonInteractive) if err := cfg.Read(config.FilePath, in, out, fsterr.Log, verboseOutput); err != nil { - return RunOpts{}, err + return nil, err } // Extract user's project configuration from the fastly.toml manifest. @@ -107,29 +109,37 @@ var Init = func(args []string, stdin io.Reader) (RunOpts, error) { // We do this here so that we can mock the values in our test suite. req, err := http.NewRequest(http.MethodGet, auth.OIDCMetadata, nil) if err != nil { - return RunOpts{}, fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) + return nil, fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) } resp, err := httpClient.Do(req) if err != nil { - return RunOpts{}, fmt.Errorf("failed to request OpenID Connect .well-known metadata: %w", err) + return nil, fmt.Errorf("failed to request OpenID Connect .well-known metadata: %w", err) } openIDConfig, err := io.ReadAll(resp.Body) if err != nil { - return RunOpts{}, fmt.Errorf("failed to read OpenID Connect .well-known metadata: %w", err) + return nil, fmt.Errorf("failed to read OpenID Connect .well-known metadata: %w", err) } _ = resp.Body.Close() var wellknown auth.WellKnownEndpoints err = json.Unmarshal(openIDConfig, &wellknown) if err != nil { - return RunOpts{}, fmt.Errorf("failed to unmarshal OpenID Connect .well-known metadata: %w", err) + return nil, fmt.Errorf("failed to unmarshal OpenID Connect .well-known metadata: %w", err) } result := make(chan auth.AuthorizationResult) router := http.NewServeMux() + verifier, err := oidc.NewCodeVerifier() + if err != nil { + return nil, fsterr.RemediationError{ + Inner: fmt.Errorf("failed to generate a code verifier for SSO authentication server: %w", err), + Remediation: auth.Remediation, + } + } authServer := &auth.Server{ DebugMode: e.DebugMode, HTTPClient: httpClient, Result: result, Router: router, + Verifier: verifier, WellKnownEndpoints: wellknown, } router.HandleFunc("/callback", authServer.HandleCallback()) @@ -142,7 +152,7 @@ var Init = func(args []string, stdin io.Reader) (RunOpts, error) { return client, err } - versioners := Versioners{ + versioners := global.Versioners{ CLI: github.New(github.Opts{ HTTPClient: httpClient, Org: "fastly", @@ -166,11 +176,11 @@ var Init = func(args []string, stdin io.Reader) (RunOpts, error) { }), } - return RunOpts{ + return &global.Data{ APIClientFactory: factory, Args: args, AuthServer: authServer, - ConfigFile: cfg, + Config: cfg, ConfigPath: config.FilePath, Env: e, ErrLog: fsterr.Log, @@ -178,9 +188,9 @@ var Init = func(args []string, stdin io.Reader) (RunOpts, error) { HTTPClient: httpClient, Manifest: &md, Opener: open.Run, - Stdin: in, - Stdout: out, + Output: out, Versioners: versioners, + Input: in, }, nil } @@ -194,23 +204,10 @@ var Init = func(args []string, stdin io.Reader) (RunOpts, error) { // The Exec helper should NOT output any error-related information to the out // io.Writer. All error-related information should be encoded into an error type // and returned to the caller. This includes usage text. -func Exec(opts RunOpts) error { - // The g will hold generally-applicable configuration parameters - // from a variety of sources, and is provided to each concrete command. - g := global.Data{ - Config: opts.ConfigFile, - ConfigPath: opts.ConfigPath, - Env: opts.Env, - ErrLog: opts.ErrLog, - ExecuteWasmTools: opts.ExecuteWasmTools, - HTTPClient: opts.HTTPClient, - Manifest: *opts.Manifest, - Output: opts.Stdout, - } - - app := configureKingpin(opts.Stdout, &g) - commands := defineCommands(app, &g, *opts.Manifest, opts.Opener, opts.AuthServer, opts.Versioners, opts.APIClientFactory) - command, commandName, err := processCommandInput(opts.Args, opts.Stdout, app, &g, commands) +func Exec(data *global.Data) error { + app := configureKingpin(data) + commands := commands.Define(app, data) + command, commandName, err := processCommandInput(data, app, commands) if err != nil { return err } @@ -230,87 +227,52 @@ func Exec(opts RunOpts) error { // FIXME: Tweak messaging before for 10.7.0 // To learn more about what data is being collected, why, and how to disable it: https://developer.fastly.com/reference/cli/ - metadataDisable, _ := strconv.ParseBool(g.Env.WasmMetadataDisable) - if slices.Contains(opts.Args, "--metadata-enable") && !metadataDisable && !g.Config.CLI.MetadataNoticeDisplayed && commandCollectsData(commandName) { - text.Important(g.Output, "The Fastly CLI is configured to collect data related to Wasm builds (e.g. compilation times, resource usage, and other non-identifying data). To learn more about our data & privacy policies visit https://www.fastly.com/trust. Join the conversation https://bit.ly/wasm-metadata") - text.Break(g.Output) - g.Config.CLI.MetadataNoticeDisplayed = true - err := g.Config.Write(g.ConfigPath) + metadataDisable, _ := strconv.ParseBool(data.Env.WasmMetadataDisable) + if slices.Contains(data.Args, "--metadata-enable") && !metadataDisable && !data.Config.CLI.MetadataNoticeDisplayed && commandCollectsData(commandName) { + text.Important(data.Output, "The Fastly CLI is configured to collect data related to Wasm builds (e.g. compilation times, resource usage, and other non-identifying data). To learn more about our data & privacy policies visit https://www.fastly.com/trust. Join the conversation https://bit.ly/wasm-metadata") + text.Break(data.Output) + data.Config.CLI.MetadataNoticeDisplayed = true + err := data.Config.Write(data.ConfigPath) if err != nil { return fmt.Errorf("failed to persist change to metadata notice: %w", err) } time.Sleep(5 * time.Second) // this message is only displayed once so give the user a chance to see it before it possibly scrolls off screen } - if g.Flags.Quiet { - opts.Manifest.File.SetQuiet(true) + if data.Flags.Quiet { + data.Manifest.File.SetQuiet(true) } - apiEndpoint, endpointSource := g.APIEndpoint() - if g.Verbose() { - displayAPIEndpoint(apiEndpoint, endpointSource, opts.Stdout) + apiEndpoint, endpointSource := data.APIEndpoint() + if data.Verbose() { + displayAPIEndpoint(apiEndpoint, endpointSource, data.Output) } - token, err := processToken(commands, commandName, opts.Manifest, &g, opts.Stdin, opts.Stdout) + token, err := processToken(commands, commandName, data) if err != nil { return fmt.Errorf("failed to process token: %w", err) } - g.APIClient, g.RTSClient, err = configureClients(token, apiEndpoint, opts.APIClientFactory, g.Flags.Debug) + data.APIClient, data.RTSClient, err = configureClients(token, apiEndpoint, data.APIClientFactory, data.Flags.Debug) if err != nil { - g.ErrLog.Add(err) + data.ErrLog.Add(err) return fmt.Errorf("error constructing client: %w", err) } - f := checkForUpdates(opts.Versioners.CLI, commandName, g.Flags.Quiet) - defer f(opts.Stdout) + f := checkForUpdates(data.Versioners.CLI, commandName, data.Flags.Quiet) + defer f(data.Output) - return command.Exec(opts.Stdin, opts.Stdout) + return command.Exec(data.Input, data.Output) } -// RunOpts represent arguments we can mock at runtime. -type RunOpts struct { - // APIClientFactory is a factory function for creating an api.Interface type. - APIClientFactory APIClientFactory - // Args are the command line arguments provided by the user. - Args []string - // AuthServer is an instance of the authentication server type. - // Used for interacting with Fastly's SSO/OAuth authentication provider. - AuthServer auth.Starter - // ConfigFile is an instance of the CLI application config. - ConfigFile config.File - // ConfigPath is the location of the CLI application config. - ConfigPath string - // Env is an instance of the supported environment variables. - Env config.Environment - // ErrLog is an instance of a error log recorder. - ErrLog fsterr.LogInterface - // ExecuteWasmTools is a function that calls wasm-tools executable. - // Designed to be used for mocking in the CLI test suite. - ExecuteWasmTools func(bin string, args []string) error - // HTTPClient is a standard HTTP client. - HTTPClient api.HTTPClient - // Manifest is the fastly.toml manifest file. - Manifest *manifest.Data - // Opener is a function that can open a browser window. - Opener func(string) error - // Stdin is the standard input destination. - Stdin io.Reader - // Stdout is the standard output destination. - Stdout io.Writer - // Versioners contains multiple software versioning checkers. - // e.g. Check for latest CLI or Viceroy version. - Versioners Versioners -} - -func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { +func configureKingpin(data *global.Data) *kingpin.Application { // Set up the main application root, including global flags, and then each // of the subcommands. Note that we deliberately don't use some of the more // advanced features of the kingpin.Application flags, like env var // bindings, because we need to do things like track where a config // parameter came from. app := kingpin.New("fastly", "A tool to interact with the Fastly API") - app.Writers(out, io.Discard) // don't let kingpin write error output + app.Writers(data.Output, io.Discard) // don't let kingpin write error output app.UsageContext(&kingpin.UsageContext{ Template: VerboseUsageTemplate, Funcs: UsageTemplateFuncs, @@ -330,17 +292,17 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // Kingpin will otherwise trigger a runtime panic 🎉 // Interestingly, short flags can be reused but only across subcommands. tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.APIToken) - app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&g.Flags.AcceptDefaults) - app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&g.Flags.Account) - app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&g.Flags.AutoYes) + app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&data.Flags.AcceptDefaults) + app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&data.Flags.Account) + app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&data.Flags.AutoYes) // IMPORTANT: `--debug` is a built-in Kingpin flag so we can't use that. - app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&g.Flags.Debug) - app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&g.Flags.Endpoint) - app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&g.Flags.NonInteractive) - app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&g.Flags.Profile) - app.Flag("quiet", "Silence all output except direct command output. This won't prevent interactive prompts (see: --accept-defaults, --auto-yes, --non-interactive)").Short('q').BoolVar(&g.Flags.Quiet) - app.Flag("token", tokenHelp).HintAction(env.Vars).Short('t').StringVar(&g.Flags.Token) - app.Flag("verbose", "Verbose logging").Short('v').BoolVar(&g.Flags.Verbose) + app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&data.Flags.Debug) + app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&data.Flags.Endpoint) + app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&data.Flags.NonInteractive) + app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&data.Flags.Profile) + app.Flag("quiet", "Silence all output except direct command output. This won't prevent interactive prompts (see: --accept-defaults, --auto-yes, --non-interactive)").Short('q').BoolVar(&data.Flags.Quiet) + app.Flag("token", tokenHelp).HintAction(env.Vars).Short('t').StringVar(&data.Flags.Token) + app.Flag("verbose", "Verbose logging").Short('v').BoolVar(&data.Flags.Verbose) return app } @@ -360,13 +322,13 @@ func configureKingpin(out io.Writer, g *global.Data) *kingpin.Application { // // Finally, we check if there is a profile override in place (e.g. set via the // --profile flag or using the `profile` field in the fastly.toml manifest). -func processToken(commands []cmd.Command, commandName string, m *manifest.Data, g *global.Data, in io.Reader, out io.Writer) (token string, err error) { +func processToken(commands []cmd.Command, commandName string, data *global.Data) (token string, err error) { warningMessage := "No API token could be found" var tokenSource lookup.Source - token, tokenSource = g.Token() + token, tokenSource = data.Token() // Check if token is from fastly.toml [profile] and refresh if expired. - tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, out, g) + tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, data) if err != nil { return token, fmt.Errorf("failed to check profile token: %w", err) } @@ -374,17 +336,17 @@ func processToken(commands []cmd.Command, commandName string, m *manifest.Data, // If there's no token available, and we need one for the invoked command, // then we'll trigger the SSO authentication flow. if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { - token, tokenSource, err = ssoAuthentication(tokenSource, token, warningMessage, commands, in, out, g) + token, tokenSource, err = ssoAuthentication(tokenSource, token, warningMessage, commands, data) if err != nil { return token, fmt.Errorf("failed to check profile token: %w", err) } } - if g.Verbose() { - displayToken(tokenSource, m.File.Profile, *g, out) + if data.Verbose() { + displayToken(tokenSource, data) } - if !g.Flags.Quiet { - checkConfigPermissions(commandName, tokenSource, out) + if !data.Flags.Quiet { + checkConfigPermissions(commandName, tokenSource, data.Output) } return token, nil @@ -397,8 +359,7 @@ func processToken(commands []cmd.Command, commandName string, m *manifest.Data, func checkProfileToken( tokenSource lookup.Source, commandName, warningMessage string, - out io.Writer, - g *global.Data, + data *global.Data, ) (lookup.Source, string, error) { if tokenSource == lookup.SourceFile && commandRequiresToken(commandName) { var ( @@ -407,14 +368,14 @@ func checkProfileToken( name, profileName string ) switch { - case g.Flags.Profile != "": // --profile - profileName = g.Flags.Profile - case g.Manifest.File.Profile != "": // `profile` field in fastly.toml - profileName = g.Manifest.File.Profile + case data.Flags.Profile != "": // --profile + profileName = data.Flags.Profile + case data.Manifest.File.Profile != "": // `profile` field in fastly.toml + profileName = data.Manifest.File.Profile default: profileName = "default" } - for name, profileData = range g.Config.Profiles { + for name, profileData = range data.Config.Profiles { if (profileName == "default" && profileData.Default) || name == profileName { // Once we find the default profile we can update the variable to be the // associated profile name so later on we can use that information to @@ -431,7 +392,7 @@ func checkProfileToken( } // Allow user to opt-in to SSO/OAuth so they can replace their long-lived token. - if shouldSkipSSO(profileName, profileData, out, g) { + if shouldSkipSSO(profileName, profileData, data) { return tokenSource, warningMessage, nil } @@ -453,18 +414,18 @@ func checkProfileToken( return tokenSource, warningMessage, nil } - if g.Flags.Verbose { - text.Info(out, "Your access token has now expired. We will attempt to refresh it") + if data.Flags.Verbose { + text.Info(data.Output, "Your access token has now expired. We will attempt to refresh it") } - accountEndpoint, _ := g.AccountEndpoint() - apiEndpoint, _ := g.APIEndpoint() + accountEndpoint, _ := data.AccountEndpoint() + apiEndpoint, _ := data.APIEndpoint() updatedJWT, err := auth.RefreshAccessToken(accountEndpoint, profileData.RefreshToken) if err != nil { return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) } - email, at, err := auth.ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, updatedJWT.AccessToken, g.Env.DebugMode, g.HTTPClient) + email, at, err := auth.ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, updatedJWT.AccessToken, data.Env.DebugMode, data.HTTPClient) if err != nil { return tokenSource, warningMessage, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) } @@ -475,7 +436,7 @@ func checkProfileToken( // So after we get the refreshed access token, we check to see if the // refresh token that was returned by the API call has also changed when // compared to the refresh token stored in the CLI config file. - current := profile.Get(profileName, g.Config.Profiles) + current := profile.Get(profileName, data.Config.Profiles) if current == nil { return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) } @@ -484,16 +445,16 @@ func checkProfileToken( refreshTokenCreated := current.RefreshTokenCreated refreshTokenTTL := current.RefreshTokenTTL if current.RefreshToken != updatedJWT.RefreshToken { - if g.Flags.Verbose { - text.Info(out, "Your refresh token was also updated") - text.Break(out) + if data.Flags.Verbose { + text.Info(data.Output, "Your refresh token was also updated") + text.Break(data.Output) } refreshToken = updatedJWT.RefreshToken refreshTokenCreated = now refreshTokenTTL = updatedJWT.RefreshExpiresIn } - ps, ok := profile.Edit(profileName, g.Config.Profiles, func(p *config.Profile) { + ps, ok := profile.Edit(profileName, data.Config.Profiles, func(p *config.Profile) { p.AccessToken = updatedJWT.AccessToken p.AccessTokenCreated = now p.AccessTokenTTL = updatedJWT.ExpiresIn @@ -509,9 +470,9 @@ func checkProfileToken( Remediation: "Run `fastly sso` to retry.", } } - g.Config.Profiles = ps - if err := g.Config.Write(g.ConfigPath); err != nil { - g.ErrLog.Add(err) + data.Config.Profiles = ps + if err := data.Config.Write(data.ConfigPath); err != nil { + data.ErrLog.Add(err) return tokenSource, warningMessage, fmt.Errorf("error saving config file: %w", err) } } @@ -523,18 +484,18 @@ func checkProfileToken( // shouldSkipSSO identifies if a config is a pre-v5 config and, if it is, // informs the user how they can use the SSO flow. It checks if the SSO // environment variable has been set and enables the SSO flow if so. -func shouldSkipSSO(profileName string, pd *config.Profile, out io.Writer, g *global.Data) bool { +func shouldSkipSSO(profileName string, pd *config.Profile, data *global.Data) bool { if noSSOToken(pd) { - return g.Env.UseSSO != "1" + return data.Env.UseSSO != "1" // FIXME: Put back messaging once SSO is GA. - // if g.Env.UseSSO == "1" { + // if data.Env.UseSSO == "1" { // return false // don't skip SSO // } - // if !g.Flags.Quiet { - // if g.Flags.Verbose { - // text.Break(out) + // if !data.Flags.Quiet { + // if data.Flags.Verbose { + // text.Break(data.Output) // } - // text.Important(out, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) + // text.Important(data.Output, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) // } // return true // skip SSO } @@ -556,18 +517,16 @@ func ssoAuthentication( tokenSource lookup.Source, token, warningMessage string, commands []cmd.Command, - in io.Reader, - out io.Writer, - g *global.Data, + data *global.Data, ) (string, lookup.Source, error) { for _, command := range commands { commandName := strings.Split(command.Name(), " ")[0] if commandName == "sso" { - if !g.Flags.AutoYes && !g.Flags.NonInteractive { - text.Important(out, "%s. We need to open your browser to authenticate you.", warningMessage) - text.Break(out) - cont, err := text.AskYesNo(out, text.BoldYellow("Do you want to continue? [y/N]: "), in) - text.Break(out) + if !data.Flags.AutoYes && !data.Flags.NonInteractive { + text.Important(data.Output, "%s. We need to open your browser to authenticate you.", warningMessage) + text.Break(data.Output) + cont, err := text.AskYesNo(data.Output, text.BoldYellow("Do you want to continue? [y/N]: "), data.Input) + text.Break(data.Output) if err != nil { return token, tokenSource, err } @@ -576,38 +535,38 @@ func ssoAuthentication( } } - g.SkipAuthPrompt = true // skip the same prompt in `sso` command flow - err := command.Exec(in, out) + data.SkipAuthPrompt = true // skip the same prompt in `sso` command flow + err := command.Exec(data.Input, data.Output) if err != nil { return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) } - text.Break(out) + text.Break(data.Output) break } } // Recheck for token (should be persisted to profile data). - token, tokenSource = g.Token() + token, tokenSource = data.Token() if tokenSource == lookup.SourceUndefined { return token, tokenSource, fsterr.ErrNoToken } return token, tokenSource, nil } -func displayToken(tokenSource lookup.Source, profileName string, g global.Data, out io.Writer) { - profileSource := determineProfile(profileName, g.Flags.Profile, g.Config.Profiles) +func displayToken(tokenSource lookup.Source, data *global.Data) { + profileSource := determineProfile(data.Manifest.File.Profile, data.Flags.Profile, data.Config.Profiles) switch tokenSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API token provided via --token\n\n") + fmt.Fprintf(data.Output, "Fastly API token provided via --token\n\n") case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API token provided via %s\n\n", env.APIToken) + fmt.Fprintf(data.Output, "Fastly API token provided via %s\n\n", env.APIToken) case lookup.SourceFile: - fmt.Fprintf(out, "Fastly API token provided via config file (profile: %s)\n\n", profileSource) + fmt.Fprintf(data.Output, "Fastly API token provided via config file (profile: %s)\n\n", profileSource) case lookup.SourceUndefined, lookup.SourceDefault: fallthrough default: - fmt.Fprintf(out, "Fastly API token not provided\n\n") + fmt.Fprintf(data.Output, "Fastly API token not provided\n\n") } } @@ -642,7 +601,7 @@ func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Wr } } -func configureClients(token, apiEndpoint string, acf APIClientFactory, debugMode bool) (apiClient api.Interface, rtsClient api.RealtimeStatsInterface, err error) { +func configureClients(token, apiEndpoint string, acf global.APIClientFactory, debugMode bool) (apiClient api.Interface, rtsClient api.RealtimeStatsInterface, err error) { apiClient, err = acf(token, apiEndpoint, debugMode) if err != nil { return nil, nil, fmt.Errorf("error constructing Fastly API client: %w", err) @@ -665,20 +624,6 @@ func checkForUpdates(av github.AssetVersioner, commandName string, quietMode boo } } -// APIClientFactory creates a Fastly API client (modeled as an api.Interface) -// from a user-provided API token. It exists as a type in order to parameterize -// the Run helper with it: in the real CLI, we can use NewClient from the Fastly -// API client library via RealClient; in tests, we can provide a mock API -// interface via MockClient. -type APIClientFactory func(token, apiEndpoint string, debugMode bool) (api.Interface, error) - -// Versioners represents all supported versioner types. -type Versioners struct { - CLI github.AssetVersioner - Viceroy github.AssetVersioner - WasmTools github.AssetVersioner -} - // determineProfile determines if the provided token was acquired via the // fastly.toml manifest, the --profile flag, or was a default profile from // within the config.toml application configuration. diff --git a/pkg/app/run_test.go b/pkg/app/run_test.go index e3b2e1322..89ce950ec 100644 --- a/pkg/app/run_test.go +++ b/pkg/app/run_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/testutil" ) @@ -122,8 +123,8 @@ whoami outC <- buf.String() }() - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - return testutil.NewRunOpts(testcase.Args, &stdout), nil + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + return testutil.MockGlobalData(testcase.Args, &stdout), nil } err := app.Run(testcase.Args, nil) if err != nil { diff --git a/pkg/app/usage.go b/pkg/app/usage.go index 5b41acc51..c485dc1dc 100644 --- a/pkg/app/usage.go +++ b/pkg/app/usage.go @@ -208,10 +208,8 @@ const VerboseUsageTemplate = `{{define "FormatCommands" -}} // processing the incoming command request from the user, as well as handling // the various places where help output can be displayed. func processCommandInput( - args []string, - out io.Writer, + data *global.Data, app *kingpin.Application, - g *global.Data, commands []cmd.Command, ) (command cmd.Command, cmdName string, err error) { // As the `help` command model gets privately added as a side-effect of @@ -219,18 +217,18 @@ func processCommandInput( // Therefore, we have to manually parse the args slice here to check for the // existence of `help --format json`, if present we print usage JSON and // exit early. - if cmd.ArgsIsHelpJSON(args) { + if cmd.ArgsIsHelpJSON(data.Args) { j, err := UsageJSON(app) if err != nil { - g.ErrLog.Add(err) + data.ErrLog.Add(err) return command, cmdName, err } - fmt.Fprintf(out, "%s", j) - return command, strings.Join(args, ""), nil + fmt.Fprintf(data.Output, "%s", j) + return command, strings.Join(data.Args, ""), nil } // Use partial application to generate help output function. - help := displayHelp(g.ErrLog, args, app, out, io.Discard) + help := displayHelp(data.ErrLog, data.Args, app, data.Output, io.Discard) // Handle parse errors and display contextual usage if possible. Due to bugs // and an obsession for lots of output side-effects in the kingpin.Parse @@ -251,14 +249,14 @@ func processCommandInput( // But it's useful to have it implemented so it's ready to roll when we do. var vars map[string]any - if cmd.IsVerboseAndQuiet(args) { + if cmd.IsVerboseAndQuiet(data.Args) { return command, cmdName, fsterr.RemediationError{ Inner: errors.New("--verbose and --quiet flag provided"), Remediation: "Either remove both --verbose and --quiet flags, or one of them.", } } - if cmd.IsHelpFlagOnly(args) && len(args) == 1 { + if cmd.IsHelpFlagOnly(data.Args) && len(data.Args) == 1 { return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: help(vars, nil), @@ -285,17 +283,17 @@ func processCommandInput( // // ctx.SelectedCommand will be nil if only a flag like --verbose or -v is // provided but with no actual command set so we check with IsGlobalFlagsOnly. - noargs := len(args) == 0 - globalFlagsOnly := cmd.IsGlobalFlagsOnly(args) - ctx, err := app.ParseContext(args) - if err != nil && !cmd.IsCompletion(args) || noargs || globalFlagsOnly { + noargs := len(data.Args) == 0 + globalFlagsOnly := cmd.IsGlobalFlagsOnly(data.Args) + ctx, err := app.ParseContext(data.Args) + if err != nil && !cmd.IsCompletion(data.Args) || noargs || globalFlagsOnly { if noargs || globalFlagsOnly { err = fmt.Errorf("command not specified") } return command, cmdName, help(vars, err) } - if len(args) == 1 && args[0] == "--" { + if len(data.Args) == 1 && data.Args[0] == "--" { return command, cmdName, fsterr.RemediationError{ Inner: errors.New("-- is invalid input when not followed by a positional argument"), Remediation: "If looking for help output try: `fastly help` for full command list or `fastly --help` for command summary.", @@ -311,14 +309,14 @@ func processCommandInput( // completion flag, as that depends on kingpin.Parse() being called, and so // the `ctx` is otherwise empty. var found bool - if !noargs && !globalFlagsOnly && !cmd.IsHelpOnly(args) && !cmd.IsHelpFlagOnly(args) && !cmd.IsCompletion(args) && !cmd.IsCompletionScript(args) { + if !noargs && !globalFlagsOnly && !cmd.IsHelpOnly(data.Args) && !cmd.IsHelpFlagOnly(data.Args) && !cmd.IsCompletion(data.Args) && !cmd.IsCompletionScript(data.Args) { command, found = cmd.Select(ctx.SelectedCommand.FullCommand(), commands) if !found { return command, cmdName, help(vars, err) } } - if cmd.ContextHasHelpFlag(ctx) && !cmd.IsHelpFlagOnly(args) { + if cmd.ContextHasHelpFlag(ctx) && !cmd.IsHelpFlagOnly(data.Args) { return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: help(vars, nil), @@ -345,21 +343,21 @@ func processCommandInput( // caller passes --completion-bash because adding a command to the arguments // list in that scenario would cause Kingpin logic to fail (as it expects the // flag to be used on its own). - if cmd.IsCompletionScript(args) { - args = append(args, "shellcomplete") + if cmd.IsCompletionScript(data.Args) { + data.Args = append(data.Args, "shellcomplete") } - cmdName, err = app.Parse(args) + cmdName, err = app.Parse(data.Args) if err != nil { return command, "", help(vars, err) } // Restore output writers - app.Writers(out, io.Discard) + app.Writers(data.Output, io.Discard) // Kingpin generates shell completion as a side-effect of kingpin.Parse() so // we allow it to call os.Exit, only if a completion flag is present. - if cmd.IsCompletion(args) || cmd.IsCompletionScript(args) { + if cmd.IsCompletion(data.Args) || cmd.IsCompletionScript(data.Args) { app.Terminate(os.Exit) return command, "shell-autocomplete", nil } @@ -372,14 +370,14 @@ func processCommandInput( return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: fsterr.RemediationError{ - Prefix: useFullHelpOutput(app, args, out).String(), + Prefix: useFullHelpOutput(app, data.Args, data.Output).String(), }, } } // Catch scenario where user wants to view help with the following format: // fastly --help - if cmd.IsHelpFlagOnly(args) { + if cmd.IsHelpFlagOnly(data.Args) { return command, cmdName, fsterr.SkipExitError{ Skip: true, Err: help(vars, nil), diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 1ca2a0f04..7378e038d 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -42,16 +42,17 @@ type WellKnownEndpoints struct { Token string `json:"token_endpoint"` } -// Starter defines the behaviour for the authentication server. -type Starter interface { +// Runner defines the behaviour for the authentication server. +type Runner interface { + // AuthURL returns a fully qualified authorization_endpoint. + // i.e. path + audience + scope + code_challenge etc. + AuthURL() (string, error) // GetResult returns the results channel GetResult() chan AuthorizationResult // SetAccountEndpoint sets the account endpoint. SetAccountEndpoint(endpoint string) // SetEndpoint sets the API endpoint. SetAPIEndpoint(endpoint string) - // SetVerifier sets the code verifier. - SetVerifier(verifier *oidc.S256Verifier) // Start starts a local server for handling authentication processing. Start() error } @@ -76,6 +77,25 @@ type Server struct { WellKnownEndpoints WellKnownEndpoints } +// AuthURL returns a fully qualified authorization_endpoint. +// i.e. path + audience + scope + code_challenge etc. +func (s Server) AuthURL() (string, error) { + challenge, err := oidc.CreateCodeChallenge(s.Verifier) + if err != nil { + return "", err + } + + authorizationURL := fmt.Sprintf( + "%s?audience=%s"+ + "&scope=openid"+ + "&response_type=code&client_id=%s"+ + "&code_challenge=%s"+ + "&code_challenge_method=S256&redirect_uri=%s", + s.WellKnownEndpoints.Auth, s.APIEndpoint, ClientID, challenge, RedirectURL) + + return authorizationURL, nil +} + // GetResult returns the result channel. func (s Server) GetResult() chan AuthorizationResult { return s.Result @@ -233,29 +253,6 @@ type AuthorizationResult struct { SessionToken string } -// GenVerifier creates a code verifier. -func GenVerifier() (*oidc.S256Verifier, error) { - return oidc.NewCodeVerifier() -} - -// GenURL constructs the required authorization_endpoint path. -func GenURL(accountEndpoint, apiEndpoint string, verifier *oidc.S256Verifier) (string, error) { - challenge, err := oidc.CreateCodeChallenge(verifier) - if err != nil { - return "", err - } - - authorizationURL := fmt.Sprintf( - "%s/realms/fastly/protocol/openid-connect/auth?audience=%s"+ - "&scope=openid"+ - "&response_type=code&client_id=%s"+ - "&code_challenge=%s"+ - "&code_challenge_method=S256&redirect_uri=%s", - accountEndpoint, apiEndpoint, ClientID, challenge, RedirectURL) - - return authorizationURL, nil -} - // GetJWT constructs and calls the token_endpoint path, returning a JWT // containing the access and refresh tokens and associated TTLs. func GetJWT(accountEndpoint, codeVerifier, authorizationCode string) (JWT, error) { diff --git a/pkg/commands/acl/acl_test.go b/pkg/commands/acl/acl_test.go index f481faaf4..1c634af8f 100644 --- a/pkg/commands/acl/acl_test.go +++ b/pkg/commands/acl/acl_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -88,8 +89,8 @@ func TestACLCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -167,8 +168,8 @@ func TestACLDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -232,8 +233,8 @@ func TestACLDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -301,8 +302,8 @@ func TestACLList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -394,8 +395,8 @@ func TestACLUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/acl/create.go b/pkg/commands/acl/create.go index aebdbcf79..bc8e46695 100644 --- a/pkg/commands/acl/create.go +++ b/pkg/commands/acl/create.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a new ACL attached to the specified service version").Alias("add") @@ -40,7 +38,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -58,7 +56,6 @@ type CreateCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name cmd.OptionalString serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -70,7 +67,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, ErrLog: c.Globals.ErrLog, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/acl/delete.go b/pkg/commands/acl/delete.go index 01a7ffab9..90bb7a025 100644 --- a/pkg/commands/acl/delete.go +++ b/pkg/commands/acl/delete.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an ACL from the specified service version").Alias("remove") @@ -39,7 +37,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -57,7 +55,6 @@ type DeleteCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -68,7 +65,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/acl/describe.go b/pkg/commands/acl/describe.go index d6e2451cb..cfb7220a4 100644 --- a/pkg/commands/acl/describe.go +++ b/pkg/commands/acl/describe.go @@ -4,20 +4,19 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Retrieve a single ACL by name for the version and service").Alias("get") @@ -35,7 +34,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -68,7 +66,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/acl/list.go b/pkg/commands/acl/list.go index 7b186d1b6..071a3a5c7 100644 --- a/pkg/commands/acl/list.go +++ b/pkg/commands/acl/list.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List ACLs") @@ -35,7 +34,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/acl/update.go b/pkg/commands/acl/update.go index 1a3acf6f8..3f7fc04a4 100644 --- a/pkg/commands/acl/update.go +++ b/pkg/commands/acl/update.go @@ -3,21 +3,20 @@ package acl import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update an ACL for a particular service and version") @@ -39,7 +38,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -57,7 +56,6 @@ type UpdateCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name string newName string serviceName cmd.OptionalServiceNameID @@ -69,7 +67,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/aclentry/aclentry_test.go b/pkg/commands/aclentry/aclentry_test.go index 7be64aa8f..226850d06 100644 --- a/pkg/commands/aclentry/aclentry_test.go +++ b/pkg/commands/aclentry/aclentry_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -77,8 +78,8 @@ func TestACLEntryCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -133,8 +134,8 @@ func TestACLEntryDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -187,8 +188,8 @@ func TestACLEntryDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -336,8 +337,8 @@ func TestACLEntryList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -468,8 +469,8 @@ func TestACLEntryUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/aclentry/create.go b/pkg/commands/aclentry/create.go index 640c7b840..a75cf349e 100644 --- a/pkg/commands/aclentry/create.go +++ b/pkg/commands/aclentry/create.go @@ -3,20 +3,19 @@ package aclentry import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Add an ACL entry to an ACL").Alias("add") @@ -30,7 +29,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -51,7 +50,6 @@ type CreateCommand struct { aclID string comment cmd.OptionalString ip cmd.OptionalString - manifest manifest.Data negated cmd.OptionalBool serviceName cmd.OptionalServiceNameID subnet cmd.OptionalInt @@ -59,7 +57,7 @@ type CreateCommand struct { // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/aclentry/delete.go b/pkg/commands/aclentry/delete.go index 3b5a98f26..04f596a79 100644 --- a/pkg/commands/aclentry/delete.go +++ b/pkg/commands/aclentry/delete.go @@ -7,17 +7,15 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an ACL entry from a specified ACL").Alias("remove") @@ -29,7 +27,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -48,13 +46,12 @@ type DeleteCommand struct { aclID string id string - manifest manifest.Data serviceName cmd.OptionalServiceNameID } // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/aclentry/describe.go b/pkg/commands/aclentry/describe.go index 37796a606..cbb1d92bf 100644 --- a/pkg/commands/aclentry/describe.go +++ b/pkg/commands/aclentry/describe.go @@ -4,20 +4,19 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Retrieve a single ACL entry").Alias("get") @@ -30,7 +29,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -50,7 +49,6 @@ type DescribeCommand struct { aclID string id string - manifest manifest.Data serviceName cmd.OptionalServiceNameID } @@ -60,7 +58,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/aclentry/list.go b/pkg/commands/aclentry/list.go index 115d41d43..55b3a634b 100644 --- a/pkg/commands/aclentry/list.go +++ b/pkg/commands/aclentry/list.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List ACLs") @@ -30,7 +29,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -55,7 +54,6 @@ type ListCommand struct { aclID string direction string - manifest manifest.Data page int perPage int serviceName cmd.OptionalServiceNameID @@ -68,7 +66,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/aclentry/update.go b/pkg/commands/aclentry/update.go index d5466c960..946512401 100644 --- a/pkg/commands/aclentry/update.go +++ b/pkg/commands/aclentry/update.go @@ -10,17 +10,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update an ACL entry for a specified ACL") @@ -36,7 +34,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -59,7 +57,6 @@ type UpdateCommand struct { file cmd.OptionalString id cmd.OptionalString ip cmd.OptionalString - manifest manifest.Data negated cmd.OptionalBool serviceName cmd.OptionalServiceNameID subnet cmd.OptionalInt @@ -67,7 +64,7 @@ type UpdateCommand struct { // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/authtoken/authtoken_test.go b/pkg/commands/authtoken/authtoken_test.go index a19a5760a..654922963 100644 --- a/pkg/commands/authtoken/authtoken_test.go +++ b/pkg/commands/authtoken/authtoken_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -70,8 +71,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -166,8 +167,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -205,8 +206,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -305,8 +306,8 @@ func TestList(t *testing.T) { }() } var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/authtoken/create.go b/pkg/commands/authtoken/create.go index 7c9992758..f677fc753 100644 --- a/pkg/commands/authtoken/create.go +++ b/pkg/commands/authtoken/create.go @@ -10,7 +10,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -19,12 +18,11 @@ import ( var Scopes = []string{"global", "purge_select", "purge_all", "global:read"} // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create an API token").Alias("add") @@ -55,7 +53,6 @@ type CreateCommand struct { cmd.Base expires time.Time - manifest manifest.Data name string password string scope []string diff --git a/pkg/commands/authtoken/delete.go b/pkg/commands/authtoken/delete.go index 7e700f114..1ccc3ecfe 100644 --- a/pkg/commands/authtoken/delete.go +++ b/pkg/commands/authtoken/delete.go @@ -11,17 +11,15 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Revoke an API token").Alias("remove") @@ -35,10 +33,9 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base - current bool - file string - id string - manifest manifest.Data + current bool + file string + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/authtoken/describe.go b/pkg/commands/authtoken/describe.go index 28f0789a2..8ac545098 100644 --- a/pkg/commands/authtoken/describe.go +++ b/pkg/commands/authtoken/describe.go @@ -10,16 +10,14 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Get the current API token").Alias("get") @@ -31,8 +29,6 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) type DescribeCommand struct { cmd.Base cmd.JSONOutput - - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/authtoken/list.go b/pkg/commands/authtoken/list.go index 027a87ffb..4b871534f 100644 --- a/pkg/commands/authtoken/list.go +++ b/pkg/commands/authtoken/list.go @@ -10,17 +10,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List API tokens") @@ -40,7 +38,6 @@ type ListCommand struct { cmd.JSONOutput customerID cmd.OptionalCustomerID - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/backend/backend_test.go b/pkg/commands/backend/backend_test.go index 5e8d390f1..601f6df20 100644 --- a/pkg/commands/backend/backend_test.go +++ b/pkg/commands/backend/backend_test.go @@ -11,6 +11,7 @@ import ( "github.com/fastly/cli/pkg/app" fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -131,8 +132,8 @@ func TestBackendCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -215,8 +216,8 @@ func TestBackendList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -257,8 +258,8 @@ func TestBackendDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -301,8 +302,8 @@ func TestBackendUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -343,8 +344,8 @@ func TestBackendDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/backend/create.go b/pkg/commands/backend/create.go index 09749e918..d529dba12 100644 --- a/pkg/commands/backend/create.go +++ b/pkg/commands/backend/create.go @@ -9,14 +9,12 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // CreateCommand calls the Fastly API to create backends. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceVersion cmd.OptionalServiceVersion @@ -52,12 +50,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a backend on a Fastly service version").Alias("add") @@ -93,7 +90,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -121,7 +118,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/backend/delete.go b/pkg/commands/backend/delete.go index 50632dbdd..d45f096dc 100644 --- a/pkg/commands/backend/delete.go +++ b/pkg/commands/backend/delete.go @@ -3,18 +3,17 @@ package backend import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete backends. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteBackendInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a backend on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -66,7 +64,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/backend/describe.go b/pkg/commands/backend/describe.go index 9cecba7ef..a838ba786 100644 --- a/pkg/commands/backend/describe.go +++ b/pkg/commands/backend/describe.go @@ -4,11 +4,11 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a backend. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetBackendInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a backend on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/backend/list.go b/pkg/commands/backend/list.go index 0fafa4ed9..634573316 100644 --- a/pkg/commands/backend/list.go +++ b/pkg/commands/backend/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list backends. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListBackendsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List backends on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/backend/update.go b/pkg/commands/backend/update.go index 1c85b7c1d..1c4f3c484 100644 --- a/pkg/commands/backend/update.go +++ b/pkg/commands/backend/update.go @@ -8,14 +8,12 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // UpdateCommand calls the Fastly API to update backends. type UpdateCommand struct { cmd.Base - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion autoClone cmd.OptionalAutoClone @@ -50,12 +48,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a backend on a Fastly service version") @@ -91,7 +88,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -118,7 +115,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/app/commands.go b/pkg/commands/commands.go similarity index 75% rename from pkg/app/commands.go rename to pkg/commands/commands.go index 7b8657338..a83351476 100644 --- a/pkg/app/commands.go +++ b/pkg/commands/commands.go @@ -1,9 +1,8 @@ -package app +package commands import ( "github.com/fastly/kingpin" - "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/acl" "github.com/fastly/cli/pkg/commands/aclentry" @@ -79,19 +78,14 @@ import ( "github.com/fastly/cli/pkg/commands/version" "github.com/fastly/cli/pkg/commands/whoami" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) -func defineCommands( +// Define constructs all the commands exposed by the CLI. +func Define( app *kingpin.Application, - g *global.Data, - m manifest.Data, - opener func(string) error, - authServer auth.Starter, - versioners Versioners, - factory APIClientFactory, + data *global.Data, ) []cmd.Command { - shellcompleteCmdRoot := shellcomplete.NewRootCommand(app, g) + shellcompleteCmdRoot := shellcomplete.NewRootCommand(app, data) // NOTE: The order commands are created are the order they appear in 'help'. // But because we need to pass the SSO command into the profile commands, it @@ -99,371 +93,371 @@ func defineCommands( // messes up the order of the commands in the `--help` output. So to make the // placement of the `sso` subcommand not look too odd we place it at the // beginning of the list of commands. - ssoCmdRoot := sso.NewRootCommand(app, g, opener, authServer) + ssoCmdRoot := sso.NewRootCommand(app, data) - aclCmdRoot := acl.NewRootCommand(app, g) - aclCreate := acl.NewCreateCommand(aclCmdRoot.CmdClause, g, m) - aclDelete := acl.NewDeleteCommand(aclCmdRoot.CmdClause, g, m) - aclDescribe := acl.NewDescribeCommand(aclCmdRoot.CmdClause, g, m) - aclList := acl.NewListCommand(aclCmdRoot.CmdClause, g, m) - aclUpdate := acl.NewUpdateCommand(aclCmdRoot.CmdClause, g, m) - aclEntryCmdRoot := aclentry.NewRootCommand(app, g) - aclEntryCreate := aclentry.NewCreateCommand(aclEntryCmdRoot.CmdClause, g, m) - aclEntryDelete := aclentry.NewDeleteCommand(aclEntryCmdRoot.CmdClause, g, m) - aclEntryDescribe := aclentry.NewDescribeCommand(aclEntryCmdRoot.CmdClause, g, m) - aclEntryList := aclentry.NewListCommand(aclEntryCmdRoot.CmdClause, g, m) - aclEntryUpdate := aclentry.NewUpdateCommand(aclEntryCmdRoot.CmdClause, g, m) - authtokenCmdRoot := authtoken.NewRootCommand(app, g) - authtokenCreate := authtoken.NewCreateCommand(authtokenCmdRoot.CmdClause, g, m) - authtokenDelete := authtoken.NewDeleteCommand(authtokenCmdRoot.CmdClause, g, m) - authtokenDescribe := authtoken.NewDescribeCommand(authtokenCmdRoot.CmdClause, g, m) - authtokenList := authtoken.NewListCommand(authtokenCmdRoot.CmdClause, g, m) - backendCmdRoot := backend.NewRootCommand(app, g) - backendCreate := backend.NewCreateCommand(backendCmdRoot.CmdClause, g, m) - backendDelete := backend.NewDeleteCommand(backendCmdRoot.CmdClause, g, m) - backendDescribe := backend.NewDescribeCommand(backendCmdRoot.CmdClause, g, m) - backendList := backend.NewListCommand(backendCmdRoot.CmdClause, g, m) - backendUpdate := backend.NewUpdateCommand(backendCmdRoot.CmdClause, g, m) - computeCmdRoot := compute.NewRootCommand(app, g) - computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, g, versioners.WasmTools) - computeDeploy := compute.NewDeployCommand(computeCmdRoot.CmdClause, g) - computeHashFiles := compute.NewHashFilesCommand(computeCmdRoot.CmdClause, g, computeBuild) - computeHashsum := compute.NewHashsumCommand(computeCmdRoot.CmdClause, g, computeBuild) - computeInit := compute.NewInitCommand(computeCmdRoot.CmdClause, g, m) - computeMetadata := compute.NewMetadataCommand(computeCmdRoot.CmdClause, g) - computePack := compute.NewPackCommand(computeCmdRoot.CmdClause, g, m) - computePublish := compute.NewPublishCommand(computeCmdRoot.CmdClause, g, computeBuild, computeDeploy) - computeServe := compute.NewServeCommand(computeCmdRoot.CmdClause, g, computeBuild, versioners.Viceroy) - computeUpdate := compute.NewUpdateCommand(computeCmdRoot.CmdClause, g, m) - computeValidate := compute.NewValidateCommand(computeCmdRoot.CmdClause, g) - configCmdRoot := config.NewRootCommand(app, g) - configstoreCmdRoot := configstore.NewRootCommand(app, g) - configstoreCreate := configstore.NewCreateCommand(configstoreCmdRoot.CmdClause, g, m) - configstoreDelete := configstore.NewDeleteCommand(configstoreCmdRoot.CmdClause, g, m) - configstoreDescribe := configstore.NewDescribeCommand(configstoreCmdRoot.CmdClause, g, m) - configstoreList := configstore.NewListCommand(configstoreCmdRoot.CmdClause, g, m) - configstoreListServices := configstore.NewListServicesCommand(configstoreCmdRoot.CmdClause, g, m) - configstoreUpdate := configstore.NewUpdateCommand(configstoreCmdRoot.CmdClause, g, m) - configstoreentryCmdRoot := configstoreentry.NewRootCommand(app, g) - configstoreentryCreate := configstoreentry.NewCreateCommand(configstoreentryCmdRoot.CmdClause, g, m) - configstoreentryDelete := configstoreentry.NewDeleteCommand(configstoreentryCmdRoot.CmdClause, g, m) - configstoreentryDescribe := configstoreentry.NewDescribeCommand(configstoreentryCmdRoot.CmdClause, g, m) - configstoreentryList := configstoreentry.NewListCommand(configstoreentryCmdRoot.CmdClause, g, m) - configstoreentryUpdate := configstoreentry.NewUpdateCommand(configstoreentryCmdRoot.CmdClause, g, m) - dictionaryCmdRoot := dictionary.NewRootCommand(app, g) - dictionaryCreate := dictionary.NewCreateCommand(dictionaryCmdRoot.CmdClause, g, m) - dictionaryDelete := dictionary.NewDeleteCommand(dictionaryCmdRoot.CmdClause, g, m) - dictionaryDescribe := dictionary.NewDescribeCommand(dictionaryCmdRoot.CmdClause, g, m) - dictionaryEntryCmdRoot := dictionaryentry.NewRootCommand(app, g) - dictionaryEntryCreate := dictionaryentry.NewCreateCommand(dictionaryEntryCmdRoot.CmdClause, g, m) - dictionaryEntryDelete := dictionaryentry.NewDeleteCommand(dictionaryEntryCmdRoot.CmdClause, g, m) - dictionaryEntryDescribe := dictionaryentry.NewDescribeCommand(dictionaryEntryCmdRoot.CmdClause, g, m) - dictionaryEntryList := dictionaryentry.NewListCommand(dictionaryEntryCmdRoot.CmdClause, g, m) - dictionaryEntryUpdate := dictionaryentry.NewUpdateCommand(dictionaryEntryCmdRoot.CmdClause, g, m) - dictionaryList := dictionary.NewListCommand(dictionaryCmdRoot.CmdClause, g, m) - dictionaryUpdate := dictionary.NewUpdateCommand(dictionaryCmdRoot.CmdClause, g, m) - domainCmdRoot := domain.NewRootCommand(app, g) - domainCreate := domain.NewCreateCommand(domainCmdRoot.CmdClause, g, m) - domainDelete := domain.NewDeleteCommand(domainCmdRoot.CmdClause, g, m) - domainDescribe := domain.NewDescribeCommand(domainCmdRoot.CmdClause, g, m) - domainList := domain.NewListCommand(domainCmdRoot.CmdClause, g, m) - domainUpdate := domain.NewUpdateCommand(domainCmdRoot.CmdClause, g, m) - domainValidate := domain.NewValidateCommand(domainCmdRoot.CmdClause, g, m) - healthcheckCmdRoot := healthcheck.NewRootCommand(app, g) - healthcheckCreate := healthcheck.NewCreateCommand(healthcheckCmdRoot.CmdClause, g, m) - healthcheckDelete := healthcheck.NewDeleteCommand(healthcheckCmdRoot.CmdClause, g, m) - healthcheckDescribe := healthcheck.NewDescribeCommand(healthcheckCmdRoot.CmdClause, g, m) - healthcheckList := healthcheck.NewListCommand(healthcheckCmdRoot.CmdClause, g, m) - healthcheckUpdate := healthcheck.NewUpdateCommand(healthcheckCmdRoot.CmdClause, g, m) - ipCmdRoot := ip.NewRootCommand(app, g) - kvstoreCmdRoot := kvstore.NewRootCommand(app, g) - kvstoreCreate := kvstore.NewCreateCommand(kvstoreCmdRoot.CmdClause, g, m) - kvstoreDelete := kvstore.NewDeleteCommand(kvstoreCmdRoot.CmdClause, g, m) - kvstoreDescribe := kvstore.NewDescribeCommand(kvstoreCmdRoot.CmdClause, g, m) - kvstoreList := kvstore.NewListCommand(kvstoreCmdRoot.CmdClause, g, m) - kvstoreentryCmdRoot := kvstoreentry.NewRootCommand(app, g) - kvstoreentryCreate := kvstoreentry.NewCreateCommand(kvstoreentryCmdRoot.CmdClause, g, m) - kvstoreentryDelete := kvstoreentry.NewDeleteCommand(kvstoreentryCmdRoot.CmdClause, g, m) - kvstoreentryDescribe := kvstoreentry.NewDescribeCommand(kvstoreentryCmdRoot.CmdClause, g, m) - kvstoreentryList := kvstoreentry.NewListCommand(kvstoreentryCmdRoot.CmdClause, g, m) - logtailCmdRoot := logtail.NewRootCommand(app, g, m) - loggingCmdRoot := logging.NewRootCommand(app, g) - loggingAzureblobCmdRoot := azureblob.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingAzureblobCreate := azureblob.NewCreateCommand(loggingAzureblobCmdRoot.CmdClause, g, m) - loggingAzureblobDelete := azureblob.NewDeleteCommand(loggingAzureblobCmdRoot.CmdClause, g, m) - loggingAzureblobDescribe := azureblob.NewDescribeCommand(loggingAzureblobCmdRoot.CmdClause, g, m) - loggingAzureblobList := azureblob.NewListCommand(loggingAzureblobCmdRoot.CmdClause, g, m) - loggingAzureblobUpdate := azureblob.NewUpdateCommand(loggingAzureblobCmdRoot.CmdClause, g, m) - loggingBigQueryCmdRoot := bigquery.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingBigQueryCreate := bigquery.NewCreateCommand(loggingBigQueryCmdRoot.CmdClause, g, m) - loggingBigQueryDelete := bigquery.NewDeleteCommand(loggingBigQueryCmdRoot.CmdClause, g, m) - loggingBigQueryDescribe := bigquery.NewDescribeCommand(loggingBigQueryCmdRoot.CmdClause, g, m) - loggingBigQueryList := bigquery.NewListCommand(loggingBigQueryCmdRoot.CmdClause, g, m) - loggingBigQueryUpdate := bigquery.NewUpdateCommand(loggingBigQueryCmdRoot.CmdClause, g, m) - loggingCloudfilesCmdRoot := cloudfiles.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingCloudfilesCreate := cloudfiles.NewCreateCommand(loggingCloudfilesCmdRoot.CmdClause, g, m) - loggingCloudfilesDelete := cloudfiles.NewDeleteCommand(loggingCloudfilesCmdRoot.CmdClause, g, m) - loggingCloudfilesDescribe := cloudfiles.NewDescribeCommand(loggingCloudfilesCmdRoot.CmdClause, g, m) - loggingCloudfilesList := cloudfiles.NewListCommand(loggingCloudfilesCmdRoot.CmdClause, g, m) - loggingCloudfilesUpdate := cloudfiles.NewUpdateCommand(loggingCloudfilesCmdRoot.CmdClause, g, m) - loggingDatadogCmdRoot := datadog.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingDatadogCreate := datadog.NewCreateCommand(loggingDatadogCmdRoot.CmdClause, g, m) - loggingDatadogDelete := datadog.NewDeleteCommand(loggingDatadogCmdRoot.CmdClause, g, m) - loggingDatadogDescribe := datadog.NewDescribeCommand(loggingDatadogCmdRoot.CmdClause, g, m) - loggingDatadogList := datadog.NewListCommand(loggingDatadogCmdRoot.CmdClause, g, m) - loggingDatadogUpdate := datadog.NewUpdateCommand(loggingDatadogCmdRoot.CmdClause, g, m) - loggingDigitaloceanCmdRoot := digitalocean.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingDigitaloceanCreate := digitalocean.NewCreateCommand(loggingDigitaloceanCmdRoot.CmdClause, g, m) - loggingDigitaloceanDelete := digitalocean.NewDeleteCommand(loggingDigitaloceanCmdRoot.CmdClause, g, m) - loggingDigitaloceanDescribe := digitalocean.NewDescribeCommand(loggingDigitaloceanCmdRoot.CmdClause, g, m) - loggingDigitaloceanList := digitalocean.NewListCommand(loggingDigitaloceanCmdRoot.CmdClause, g, m) - loggingDigitaloceanUpdate := digitalocean.NewUpdateCommand(loggingDigitaloceanCmdRoot.CmdClause, g, m) - loggingElasticsearchCmdRoot := elasticsearch.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingElasticsearchCreate := elasticsearch.NewCreateCommand(loggingElasticsearchCmdRoot.CmdClause, g, m) - loggingElasticsearchDelete := elasticsearch.NewDeleteCommand(loggingElasticsearchCmdRoot.CmdClause, g, m) - loggingElasticsearchDescribe := elasticsearch.NewDescribeCommand(loggingElasticsearchCmdRoot.CmdClause, g, m) - loggingElasticsearchList := elasticsearch.NewListCommand(loggingElasticsearchCmdRoot.CmdClause, g, m) - loggingElasticsearchUpdate := elasticsearch.NewUpdateCommand(loggingElasticsearchCmdRoot.CmdClause, g, m) - loggingFtpCmdRoot := ftp.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingFtpCreate := ftp.NewCreateCommand(loggingFtpCmdRoot.CmdClause, g, m) - loggingFtpDelete := ftp.NewDeleteCommand(loggingFtpCmdRoot.CmdClause, g, m) - loggingFtpDescribe := ftp.NewDescribeCommand(loggingFtpCmdRoot.CmdClause, g, m) - loggingFtpList := ftp.NewListCommand(loggingFtpCmdRoot.CmdClause, g, m) - loggingFtpUpdate := ftp.NewUpdateCommand(loggingFtpCmdRoot.CmdClause, g, m) - loggingGcsCmdRoot := gcs.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingGcsCreate := gcs.NewCreateCommand(loggingGcsCmdRoot.CmdClause, g, m) - loggingGcsDelete := gcs.NewDeleteCommand(loggingGcsCmdRoot.CmdClause, g, m) - loggingGcsDescribe := gcs.NewDescribeCommand(loggingGcsCmdRoot.CmdClause, g, m) - loggingGcsList := gcs.NewListCommand(loggingGcsCmdRoot.CmdClause, g, m) - loggingGcsUpdate := gcs.NewUpdateCommand(loggingGcsCmdRoot.CmdClause, g, m) - loggingGooglepubsubCmdRoot := googlepubsub.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingGooglepubsubCreate := googlepubsub.NewCreateCommand(loggingGooglepubsubCmdRoot.CmdClause, g, m) - loggingGooglepubsubDelete := googlepubsub.NewDeleteCommand(loggingGooglepubsubCmdRoot.CmdClause, g, m) - loggingGooglepubsubDescribe := googlepubsub.NewDescribeCommand(loggingGooglepubsubCmdRoot.CmdClause, g, m) - loggingGooglepubsubList := googlepubsub.NewListCommand(loggingGooglepubsubCmdRoot.CmdClause, g, m) - loggingGooglepubsubUpdate := googlepubsub.NewUpdateCommand(loggingGooglepubsubCmdRoot.CmdClause, g, m) - loggingHerokuCmdRoot := heroku.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingHerokuCreate := heroku.NewCreateCommand(loggingHerokuCmdRoot.CmdClause, g, m) - loggingHerokuDelete := heroku.NewDeleteCommand(loggingHerokuCmdRoot.CmdClause, g, m) - loggingHerokuDescribe := heroku.NewDescribeCommand(loggingHerokuCmdRoot.CmdClause, g, m) - loggingHerokuList := heroku.NewListCommand(loggingHerokuCmdRoot.CmdClause, g, m) - loggingHerokuUpdate := heroku.NewUpdateCommand(loggingHerokuCmdRoot.CmdClause, g, m) - loggingHoneycombCmdRoot := honeycomb.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingHoneycombCreate := honeycomb.NewCreateCommand(loggingHoneycombCmdRoot.CmdClause, g, m) - loggingHoneycombDelete := honeycomb.NewDeleteCommand(loggingHoneycombCmdRoot.CmdClause, g, m) - loggingHoneycombDescribe := honeycomb.NewDescribeCommand(loggingHoneycombCmdRoot.CmdClause, g, m) - loggingHoneycombList := honeycomb.NewListCommand(loggingHoneycombCmdRoot.CmdClause, g, m) - loggingHoneycombUpdate := honeycomb.NewUpdateCommand(loggingHoneycombCmdRoot.CmdClause, g, m) - loggingHTTPSCmdRoot := https.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingHTTPSCreate := https.NewCreateCommand(loggingHTTPSCmdRoot.CmdClause, g, m) - loggingHTTPSDelete := https.NewDeleteCommand(loggingHTTPSCmdRoot.CmdClause, g, m) - loggingHTTPSDescribe := https.NewDescribeCommand(loggingHTTPSCmdRoot.CmdClause, g, m) - loggingHTTPSList := https.NewListCommand(loggingHTTPSCmdRoot.CmdClause, g, m) - loggingHTTPSUpdate := https.NewUpdateCommand(loggingHTTPSCmdRoot.CmdClause, g, m) - loggingKafkaCmdRoot := kafka.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingKafkaCreate := kafka.NewCreateCommand(loggingKafkaCmdRoot.CmdClause, g, m) - loggingKafkaDelete := kafka.NewDeleteCommand(loggingKafkaCmdRoot.CmdClause, g, m) - loggingKafkaDescribe := kafka.NewDescribeCommand(loggingKafkaCmdRoot.CmdClause, g, m) - loggingKafkaList := kafka.NewListCommand(loggingKafkaCmdRoot.CmdClause, g, m) - loggingKafkaUpdate := kafka.NewUpdateCommand(loggingKafkaCmdRoot.CmdClause, g, m) - loggingKinesisCmdRoot := kinesis.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingKinesisCreate := kinesis.NewCreateCommand(loggingKinesisCmdRoot.CmdClause, g, m) - loggingKinesisDelete := kinesis.NewDeleteCommand(loggingKinesisCmdRoot.CmdClause, g, m) - loggingKinesisDescribe := kinesis.NewDescribeCommand(loggingKinesisCmdRoot.CmdClause, g, m) - loggingKinesisList := kinesis.NewListCommand(loggingKinesisCmdRoot.CmdClause, g, m) - loggingKinesisUpdate := kinesis.NewUpdateCommand(loggingKinesisCmdRoot.CmdClause, g, m) - loggingLogglyCmdRoot := loggly.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingLogglyCreate := loggly.NewCreateCommand(loggingLogglyCmdRoot.CmdClause, g, m) - loggingLogglyDelete := loggly.NewDeleteCommand(loggingLogglyCmdRoot.CmdClause, g, m) - loggingLogglyDescribe := loggly.NewDescribeCommand(loggingLogglyCmdRoot.CmdClause, g, m) - loggingLogglyList := loggly.NewListCommand(loggingLogglyCmdRoot.CmdClause, g, m) - loggingLogglyUpdate := loggly.NewUpdateCommand(loggingLogglyCmdRoot.CmdClause, g, m) - loggingLogshuttleCmdRoot := logshuttle.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingLogshuttleCreate := logshuttle.NewCreateCommand(loggingLogshuttleCmdRoot.CmdClause, g, m) - loggingLogshuttleDelete := logshuttle.NewDeleteCommand(loggingLogshuttleCmdRoot.CmdClause, g, m) - loggingLogshuttleDescribe := logshuttle.NewDescribeCommand(loggingLogshuttleCmdRoot.CmdClause, g, m) - loggingLogshuttleList := logshuttle.NewListCommand(loggingLogshuttleCmdRoot.CmdClause, g, m) - loggingLogshuttleUpdate := logshuttle.NewUpdateCommand(loggingLogshuttleCmdRoot.CmdClause, g, m) - loggingNewRelicCmdRoot := newrelic.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingNewRelicCreate := newrelic.NewCreateCommand(loggingNewRelicCmdRoot.CmdClause, g, m) - loggingNewRelicDelete := newrelic.NewDeleteCommand(loggingNewRelicCmdRoot.CmdClause, g, m) - loggingNewRelicDescribe := newrelic.NewDescribeCommand(loggingNewRelicCmdRoot.CmdClause, g, m) - loggingNewRelicList := newrelic.NewListCommand(loggingNewRelicCmdRoot.CmdClause, g, m) - loggingNewRelicUpdate := newrelic.NewUpdateCommand(loggingNewRelicCmdRoot.CmdClause, g, m) - loggingNewRelicOTLPCmdRoot := newrelicotlp.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingNewRelicOTLPCreate := newrelicotlp.NewCreateCommand(loggingNewRelicOTLPCmdRoot.CmdClause, g, m) - loggingNewRelicOTLPDelete := newrelicotlp.NewDeleteCommand(loggingNewRelicOTLPCmdRoot.CmdClause, g, m) - loggingNewRelicOTLPDescribe := newrelicotlp.NewDescribeCommand(loggingNewRelicOTLPCmdRoot.CmdClause, g, m) - loggingNewRelicOTLPList := newrelicotlp.NewListCommand(loggingNewRelicOTLPCmdRoot.CmdClause, g, m) - loggingNewRelicOTLPUpdate := newrelicotlp.NewUpdateCommand(loggingNewRelicOTLPCmdRoot.CmdClause, g, m) - loggingOpenstackCmdRoot := openstack.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingOpenstackCreate := openstack.NewCreateCommand(loggingOpenstackCmdRoot.CmdClause, g, m) - loggingOpenstackDelete := openstack.NewDeleteCommand(loggingOpenstackCmdRoot.CmdClause, g, m) - loggingOpenstackDescribe := openstack.NewDescribeCommand(loggingOpenstackCmdRoot.CmdClause, g, m) - loggingOpenstackList := openstack.NewListCommand(loggingOpenstackCmdRoot.CmdClause, g, m) - loggingOpenstackUpdate := openstack.NewUpdateCommand(loggingOpenstackCmdRoot.CmdClause, g, m) - loggingPapertrailCmdRoot := papertrail.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingPapertrailCreate := papertrail.NewCreateCommand(loggingPapertrailCmdRoot.CmdClause, g, m) - loggingPapertrailDelete := papertrail.NewDeleteCommand(loggingPapertrailCmdRoot.CmdClause, g, m) - loggingPapertrailDescribe := papertrail.NewDescribeCommand(loggingPapertrailCmdRoot.CmdClause, g, m) - loggingPapertrailList := papertrail.NewListCommand(loggingPapertrailCmdRoot.CmdClause, g, m) - loggingPapertrailUpdate := papertrail.NewUpdateCommand(loggingPapertrailCmdRoot.CmdClause, g, m) - loggingS3CmdRoot := s3.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingS3Create := s3.NewCreateCommand(loggingS3CmdRoot.CmdClause, g, m) - loggingS3Delete := s3.NewDeleteCommand(loggingS3CmdRoot.CmdClause, g, m) - loggingS3Describe := s3.NewDescribeCommand(loggingS3CmdRoot.CmdClause, g, m) - loggingS3List := s3.NewListCommand(loggingS3CmdRoot.CmdClause, g, m) - loggingS3Update := s3.NewUpdateCommand(loggingS3CmdRoot.CmdClause, g, m) - loggingScalyrCmdRoot := scalyr.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingScalyrCreate := scalyr.NewCreateCommand(loggingScalyrCmdRoot.CmdClause, g, m) - loggingScalyrDelete := scalyr.NewDeleteCommand(loggingScalyrCmdRoot.CmdClause, g, m) - loggingScalyrDescribe := scalyr.NewDescribeCommand(loggingScalyrCmdRoot.CmdClause, g, m) - loggingScalyrList := scalyr.NewListCommand(loggingScalyrCmdRoot.CmdClause, g, m) - loggingScalyrUpdate := scalyr.NewUpdateCommand(loggingScalyrCmdRoot.CmdClause, g, m) - loggingSftpCmdRoot := sftp.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingSftpCreate := sftp.NewCreateCommand(loggingSftpCmdRoot.CmdClause, g, m) - loggingSftpDelete := sftp.NewDeleteCommand(loggingSftpCmdRoot.CmdClause, g, m) - loggingSftpDescribe := sftp.NewDescribeCommand(loggingSftpCmdRoot.CmdClause, g, m) - loggingSftpList := sftp.NewListCommand(loggingSftpCmdRoot.CmdClause, g, m) - loggingSftpUpdate := sftp.NewUpdateCommand(loggingSftpCmdRoot.CmdClause, g, m) - loggingSplunkCmdRoot := splunk.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingSplunkCreate := splunk.NewCreateCommand(loggingSplunkCmdRoot.CmdClause, g, m) - loggingSplunkDelete := splunk.NewDeleteCommand(loggingSplunkCmdRoot.CmdClause, g, m) - loggingSplunkDescribe := splunk.NewDescribeCommand(loggingSplunkCmdRoot.CmdClause, g, m) - loggingSplunkList := splunk.NewListCommand(loggingSplunkCmdRoot.CmdClause, g, m) - loggingSplunkUpdate := splunk.NewUpdateCommand(loggingSplunkCmdRoot.CmdClause, g, m) - loggingSumologicCmdRoot := sumologic.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingSumologicCreate := sumologic.NewCreateCommand(loggingSumologicCmdRoot.CmdClause, g, m) - loggingSumologicDelete := sumologic.NewDeleteCommand(loggingSumologicCmdRoot.CmdClause, g, m) - loggingSumologicDescribe := sumologic.NewDescribeCommand(loggingSumologicCmdRoot.CmdClause, g, m) - loggingSumologicList := sumologic.NewListCommand(loggingSumologicCmdRoot.CmdClause, g, m) - loggingSumologicUpdate := sumologic.NewUpdateCommand(loggingSumologicCmdRoot.CmdClause, g, m) - loggingSyslogCmdRoot := syslog.NewRootCommand(loggingCmdRoot.CmdClause, g) - loggingSyslogCreate := syslog.NewCreateCommand(loggingSyslogCmdRoot.CmdClause, g, m) - loggingSyslogDelete := syslog.NewDeleteCommand(loggingSyslogCmdRoot.CmdClause, g, m) - loggingSyslogDescribe := syslog.NewDescribeCommand(loggingSyslogCmdRoot.CmdClause, g, m) - loggingSyslogList := syslog.NewListCommand(loggingSyslogCmdRoot.CmdClause, g, m) - loggingSyslogUpdate := syslog.NewUpdateCommand(loggingSyslogCmdRoot.CmdClause, g, m) - popCmdRoot := pop.NewRootCommand(app, g) - productsCmdRoot := products.NewRootCommand(app, g, m) - profileCmdRoot := profile.NewRootCommand(app, g) - profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(factory), g, ssoCmdRoot) - profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, g) - profileList := profile.NewListCommand(profileCmdRoot.CmdClause, g) - profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, g) - profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, g) - profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, profile.APIClientFactory(factory), g, ssoCmdRoot) - purgeCmdRoot := purge.NewRootCommand(app, g, m) - rateLimitCmdRoot := ratelimit.NewRootCommand(app, g) - rateLimitCreate := ratelimit.NewCreateCommand(rateLimitCmdRoot.CmdClause, g, m) - rateLimitDelete := ratelimit.NewDeleteCommand(rateLimitCmdRoot.CmdClause, g, m) - rateLimitDescribe := ratelimit.NewDescribeCommand(rateLimitCmdRoot.CmdClause, g, m) - rateLimitList := ratelimit.NewListCommand(rateLimitCmdRoot.CmdClause, g, m) - rateLimitUpdate := ratelimit.NewUpdateCommand(rateLimitCmdRoot.CmdClause, g, m) - resourcelinkCmdRoot := resourcelink.NewRootCommand(app, g) - resourcelinkCreate := resourcelink.NewCreateCommand(resourcelinkCmdRoot.CmdClause, g, m) - resourcelinkDelete := resourcelink.NewDeleteCommand(resourcelinkCmdRoot.CmdClause, g, m) - resourcelinkDescribe := resourcelink.NewDescribeCommand(resourcelinkCmdRoot.CmdClause, g, m) - resourcelinkList := resourcelink.NewListCommand(resourcelinkCmdRoot.CmdClause, g, m) - resourcelinkUpdate := resourcelink.NewUpdateCommand(resourcelinkCmdRoot.CmdClause, g, m) - secretstoreCmdRoot := secretstore.NewRootCommand(app, g) - secretstoreCreate := secretstore.NewCreateCommand(secretstoreCmdRoot.CmdClause, g, m) - secretstoreDescribe := secretstore.NewDescribeCommand(secretstoreCmdRoot.CmdClause, g, m) - secretstoreDelete := secretstore.NewDeleteCommand(secretstoreCmdRoot.CmdClause, g, m) - secretstoreList := secretstore.NewListCommand(secretstoreCmdRoot.CmdClause, g, m) - secretstoreentryCmdRoot := secretstoreentry.NewRootCommand(app, g) - secretstoreentryCreate := secretstoreentry.NewCreateCommand(secretstoreentryCmdRoot.CmdClause, g, m) - secretstoreentryDescribe := secretstoreentry.NewDescribeCommand(secretstoreentryCmdRoot.CmdClause, g, m) - secretstoreentryDelete := secretstoreentry.NewDeleteCommand(secretstoreentryCmdRoot.CmdClause, g, m) - secretstoreentryList := secretstoreentry.NewListCommand(secretstoreentryCmdRoot.CmdClause, g, m) - serviceCmdRoot := service.NewRootCommand(app, g) - serviceCreate := service.NewCreateCommand(serviceCmdRoot.CmdClause, g) - serviceDelete := service.NewDeleteCommand(serviceCmdRoot.CmdClause, g, m) - serviceDescribe := service.NewDescribeCommand(serviceCmdRoot.CmdClause, g, m) - serviceList := service.NewListCommand(serviceCmdRoot.CmdClause, g) - serviceSearch := service.NewSearchCommand(serviceCmdRoot.CmdClause, g, m) - serviceUpdate := service.NewUpdateCommand(serviceCmdRoot.CmdClause, g, m) - serviceauthCmdRoot := serviceauth.NewRootCommand(app, g) - serviceauthCreate := serviceauth.NewCreateCommand(serviceauthCmdRoot.CmdClause, g, m) - serviceauthDelete := serviceauth.NewDeleteCommand(serviceauthCmdRoot.CmdClause, g, m) - serviceauthDescribe := serviceauth.NewDescribeCommand(serviceauthCmdRoot.CmdClause, g, m) - serviceauthList := serviceauth.NewListCommand(serviceauthCmdRoot.CmdClause, g) - serviceauthUpdate := serviceauth.NewUpdateCommand(serviceauthCmdRoot.CmdClause, g, m) - serviceVersionCmdRoot := serviceversion.NewRootCommand(app, g) - serviceVersionActivate := serviceversion.NewActivateCommand(serviceVersionCmdRoot.CmdClause, g, m) - serviceVersionClone := serviceversion.NewCloneCommand(serviceVersionCmdRoot.CmdClause, g, m) - serviceVersionDeactivate := serviceversion.NewDeactivateCommand(serviceVersionCmdRoot.CmdClause, g, m) - serviceVersionList := serviceversion.NewListCommand(serviceVersionCmdRoot.CmdClause, g, m) - serviceVersionLock := serviceversion.NewLockCommand(serviceVersionCmdRoot.CmdClause, g, m) - serviceVersionUpdate := serviceversion.NewUpdateCommand(serviceVersionCmdRoot.CmdClause, g, m) - statsCmdRoot := stats.NewRootCommand(app, g) - statsHistorical := stats.NewHistoricalCommand(statsCmdRoot.CmdClause, g, m) - statsRealtime := stats.NewRealtimeCommand(statsCmdRoot.CmdClause, g, m) - statsRegions := stats.NewRegionsCommand(statsCmdRoot.CmdClause, g) - tlsConfigCmdRoot := tlsconfig.NewRootCommand(app, g) - tlsConfigDescribe := tlsconfig.NewDescribeCommand(tlsConfigCmdRoot.CmdClause, g, m) - tlsConfigList := tlsconfig.NewListCommand(tlsConfigCmdRoot.CmdClause, g, m) - tlsConfigUpdate := tlsconfig.NewUpdateCommand(tlsConfigCmdRoot.CmdClause, g, m) - tlsCustomCmdRoot := tlscustom.NewRootCommand(app, g) - tlsCustomActivationCmdRoot := tlscustomactivation.NewRootCommand(tlsCustomCmdRoot.CmdClause, g) - tlsCustomActivationCreate := tlscustomactivation.NewCreateCommand(tlsCustomActivationCmdRoot.CmdClause, g, m) - tlsCustomActivationDelete := tlscustomactivation.NewDeleteCommand(tlsCustomActivationCmdRoot.CmdClause, g, m) - tlsCustomActivationDescribe := tlscustomactivation.NewDescribeCommand(tlsCustomActivationCmdRoot.CmdClause, g, m) - tlsCustomActivationList := tlscustomactivation.NewListCommand(tlsCustomActivationCmdRoot.CmdClause, g, m) - tlsCustomActivationUpdate := tlscustomactivation.NewUpdateCommand(tlsCustomActivationCmdRoot.CmdClause, g, m) - tlsCustomCertificateCmdRoot := tlscustomcertificate.NewRootCommand(tlsCustomCmdRoot.CmdClause, g) - tlsCustomCertificateCreate := tlscustomcertificate.NewCreateCommand(tlsCustomCertificateCmdRoot.CmdClause, g, m) - tlsCustomCertificateDelete := tlscustomcertificate.NewDeleteCommand(tlsCustomCertificateCmdRoot.CmdClause, g, m) - tlsCustomCertificateDescribe := tlscustomcertificate.NewDescribeCommand(tlsCustomCertificateCmdRoot.CmdClause, g, m) - tlsCustomCertificateList := tlscustomcertificate.NewListCommand(tlsCustomCertificateCmdRoot.CmdClause, g, m) - tlsCustomCertificateUpdate := tlscustomcertificate.NewUpdateCommand(tlsCustomCertificateCmdRoot.CmdClause, g, m) - tlsCustomDomainCmdRoot := tlscustomdomain.NewRootCommand(tlsCustomCmdRoot.CmdClause, g) - tlsCustomDomainList := tlscustomdomain.NewListCommand(tlsCustomDomainCmdRoot.CmdClause, g, m) - tlsCustomPrivateKeyCmdRoot := tlscustomprivatekey.NewRootCommand(tlsCustomCmdRoot.CmdClause, g) - tlsCustomPrivateKeyCreate := tlscustomprivatekey.NewCreateCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, g, m) - tlsCustomPrivateKeyDelete := tlscustomprivatekey.NewDeleteCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, g, m) - tlsCustomPrivateKeyDescribe := tlscustomprivatekey.NewDescribeCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, g, m) - tlsCustomPrivateKeyList := tlscustomprivatekey.NewListCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, g, m) - tlsPlatformCmdRoot := tlsplatform.NewRootCommand(app, g) - tlsPlatformCreate := tlsplatform.NewCreateCommand(tlsPlatformCmdRoot.CmdClause, g, m) - tlsPlatformDelete := tlsplatform.NewDeleteCommand(tlsPlatformCmdRoot.CmdClause, g, m) - tlsPlatformDescribe := tlsplatform.NewDescribeCommand(tlsPlatformCmdRoot.CmdClause, g, m) - tlsPlatformList := tlsplatform.NewListCommand(tlsPlatformCmdRoot.CmdClause, g, m) - tlsPlatformUpdate := tlsplatform.NewUpdateCommand(tlsPlatformCmdRoot.CmdClause, g, m) - tlsSubscriptionCmdRoot := tlssubscription.NewRootCommand(app, g) - tlsSubscriptionCreate := tlssubscription.NewCreateCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) - tlsSubscriptionDelete := tlssubscription.NewDeleteCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) - tlsSubscriptionDescribe := tlssubscription.NewDescribeCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) - tlsSubscriptionList := tlssubscription.NewListCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) - tlsSubscriptionUpdate := tlssubscription.NewUpdateCommand(tlsSubscriptionCmdRoot.CmdClause, g, m) - updateRoot := update.NewRootCommand(app, g.ConfigPath, versioners.CLI, g) - userCmdRoot := user.NewRootCommand(app, g) - userCreate := user.NewCreateCommand(userCmdRoot.CmdClause, g, m) - userDelete := user.NewDeleteCommand(userCmdRoot.CmdClause, g, m) - userDescribe := user.NewDescribeCommand(userCmdRoot.CmdClause, g, m) - userList := user.NewListCommand(userCmdRoot.CmdClause, g, m) - userUpdate := user.NewUpdateCommand(userCmdRoot.CmdClause, g, m) - vclCmdRoot := vcl.NewRootCommand(app, g) - vclConditionCmdRoot := condition.NewRootCommand(vclCmdRoot.CmdClause, g) - vclConditionCreate := condition.NewCreateCommand(vclConditionCmdRoot.CmdClause, g, m) - vclConditionDelete := condition.NewDeleteCommand(vclConditionCmdRoot.CmdClause, g, m) - vclConditionDescribe := condition.NewDescribeCommand(vclConditionCmdRoot.CmdClause, g, m) - vclConditionList := condition.NewListCommand(vclConditionCmdRoot.CmdClause, g, m) - vclConditionUpdate := condition.NewUpdateCommand(vclConditionCmdRoot.CmdClause, g, m) - vclCustomCmdRoot := custom.NewRootCommand(vclCmdRoot.CmdClause, g) - vclCustomCreate := custom.NewCreateCommand(vclCustomCmdRoot.CmdClause, g, m) - vclCustomDelete := custom.NewDeleteCommand(vclCustomCmdRoot.CmdClause, g, m) - vclCustomDescribe := custom.NewDescribeCommand(vclCustomCmdRoot.CmdClause, g, m) - vclCustomList := custom.NewListCommand(vclCustomCmdRoot.CmdClause, g, m) - vclCustomUpdate := custom.NewUpdateCommand(vclCustomCmdRoot.CmdClause, g, m) - vclSnippetCmdRoot := snippet.NewRootCommand(vclCmdRoot.CmdClause, g) - vclSnippetCreate := snippet.NewCreateCommand(vclSnippetCmdRoot.CmdClause, g, m) - vclSnippetDelete := snippet.NewDeleteCommand(vclSnippetCmdRoot.CmdClause, g, m) - vclSnippetDescribe := snippet.NewDescribeCommand(vclSnippetCmdRoot.CmdClause, g, m) - vclSnippetList := snippet.NewListCommand(vclSnippetCmdRoot.CmdClause, g, m) - vclSnippetUpdate := snippet.NewUpdateCommand(vclSnippetCmdRoot.CmdClause, g, m) - versionCmdRoot := version.NewRootCommand(app, versioners.Viceroy) - whoamiCmdRoot := whoami.NewRootCommand(app, g) + aclCmdRoot := acl.NewRootCommand(app, data) + aclCreate := acl.NewCreateCommand(aclCmdRoot.CmdClause, data) + aclDelete := acl.NewDeleteCommand(aclCmdRoot.CmdClause, data) + aclDescribe := acl.NewDescribeCommand(aclCmdRoot.CmdClause, data) + aclList := acl.NewListCommand(aclCmdRoot.CmdClause, data) + aclUpdate := acl.NewUpdateCommand(aclCmdRoot.CmdClause, data) + aclEntryCmdRoot := aclentry.NewRootCommand(app, data) + aclEntryCreate := aclentry.NewCreateCommand(aclEntryCmdRoot.CmdClause, data) + aclEntryDelete := aclentry.NewDeleteCommand(aclEntryCmdRoot.CmdClause, data) + aclEntryDescribe := aclentry.NewDescribeCommand(aclEntryCmdRoot.CmdClause, data) + aclEntryList := aclentry.NewListCommand(aclEntryCmdRoot.CmdClause, data) + aclEntryUpdate := aclentry.NewUpdateCommand(aclEntryCmdRoot.CmdClause, data) + authtokenCmdRoot := authtoken.NewRootCommand(app, data) + authtokenCreate := authtoken.NewCreateCommand(authtokenCmdRoot.CmdClause, data) + authtokenDelete := authtoken.NewDeleteCommand(authtokenCmdRoot.CmdClause, data) + authtokenDescribe := authtoken.NewDescribeCommand(authtokenCmdRoot.CmdClause, data) + authtokenList := authtoken.NewListCommand(authtokenCmdRoot.CmdClause, data) + backendCmdRoot := backend.NewRootCommand(app, data) + backendCreate := backend.NewCreateCommand(backendCmdRoot.CmdClause, data) + backendDelete := backend.NewDeleteCommand(backendCmdRoot.CmdClause, data) + backendDescribe := backend.NewDescribeCommand(backendCmdRoot.CmdClause, data) + backendList := backend.NewListCommand(backendCmdRoot.CmdClause, data) + backendUpdate := backend.NewUpdateCommand(backendCmdRoot.CmdClause, data) + computeCmdRoot := compute.NewRootCommand(app, data) + computeBuild := compute.NewBuildCommand(computeCmdRoot.CmdClause, data) + computeDeploy := compute.NewDeployCommand(computeCmdRoot.CmdClause, data) + computeHashFiles := compute.NewHashFilesCommand(computeCmdRoot.CmdClause, data, computeBuild) + computeHashsum := compute.NewHashsumCommand(computeCmdRoot.CmdClause, data, computeBuild) + computeInit := compute.NewInitCommand(computeCmdRoot.CmdClause, data) + computeMetadata := compute.NewMetadataCommand(computeCmdRoot.CmdClause, data) + computePack := compute.NewPackCommand(computeCmdRoot.CmdClause, data) + computePublish := compute.NewPublishCommand(computeCmdRoot.CmdClause, data, computeBuild, computeDeploy) + computeServe := compute.NewServeCommand(computeCmdRoot.CmdClause, data, computeBuild) + computeUpdate := compute.NewUpdateCommand(computeCmdRoot.CmdClause, data) + computeValidate := compute.NewValidateCommand(computeCmdRoot.CmdClause, data) + configCmdRoot := config.NewRootCommand(app, data) + configstoreCmdRoot := configstore.NewRootCommand(app, data) + configstoreCreate := configstore.NewCreateCommand(configstoreCmdRoot.CmdClause, data) + configstoreDelete := configstore.NewDeleteCommand(configstoreCmdRoot.CmdClause, data) + configstoreDescribe := configstore.NewDescribeCommand(configstoreCmdRoot.CmdClause, data) + configstoreList := configstore.NewListCommand(configstoreCmdRoot.CmdClause, data) + configstoreListServices := configstore.NewListServicesCommand(configstoreCmdRoot.CmdClause, data) + configstoreUpdate := configstore.NewUpdateCommand(configstoreCmdRoot.CmdClause, data) + configstoreentryCmdRoot := configstoreentry.NewRootCommand(app, data) + configstoreentryCreate := configstoreentry.NewCreateCommand(configstoreentryCmdRoot.CmdClause, data) + configstoreentryDelete := configstoreentry.NewDeleteCommand(configstoreentryCmdRoot.CmdClause, data) + configstoreentryDescribe := configstoreentry.NewDescribeCommand(configstoreentryCmdRoot.CmdClause, data) + configstoreentryList := configstoreentry.NewListCommand(configstoreentryCmdRoot.CmdClause, data) + configstoreentryUpdate := configstoreentry.NewUpdateCommand(configstoreentryCmdRoot.CmdClause, data) + dictionaryCmdRoot := dictionary.NewRootCommand(app, data) + dictionaryCreate := dictionary.NewCreateCommand(dictionaryCmdRoot.CmdClause, data) + dictionaryDelete := dictionary.NewDeleteCommand(dictionaryCmdRoot.CmdClause, data) + dictionaryDescribe := dictionary.NewDescribeCommand(dictionaryCmdRoot.CmdClause, data) + dictionaryEntryCmdRoot := dictionaryentry.NewRootCommand(app, data) + dictionaryEntryCreate := dictionaryentry.NewCreateCommand(dictionaryEntryCmdRoot.CmdClause, data) + dictionaryEntryDelete := dictionaryentry.NewDeleteCommand(dictionaryEntryCmdRoot.CmdClause, data) + dictionaryEntryDescribe := dictionaryentry.NewDescribeCommand(dictionaryEntryCmdRoot.CmdClause, data) + dictionaryEntryList := dictionaryentry.NewListCommand(dictionaryEntryCmdRoot.CmdClause, data) + dictionaryEntryUpdate := dictionaryentry.NewUpdateCommand(dictionaryEntryCmdRoot.CmdClause, data) + dictionaryList := dictionary.NewListCommand(dictionaryCmdRoot.CmdClause, data) + dictionaryUpdate := dictionary.NewUpdateCommand(dictionaryCmdRoot.CmdClause, data) + domainCmdRoot := domain.NewRootCommand(app, data) + domainCreate := domain.NewCreateCommand(domainCmdRoot.CmdClause, data) + domainDelete := domain.NewDeleteCommand(domainCmdRoot.CmdClause, data) + domainDescribe := domain.NewDescribeCommand(domainCmdRoot.CmdClause, data) + domainList := domain.NewListCommand(domainCmdRoot.CmdClause, data) + domainUpdate := domain.NewUpdateCommand(domainCmdRoot.CmdClause, data) + domainValidate := domain.NewValidateCommand(domainCmdRoot.CmdClause, data) + healthcheckCmdRoot := healthcheck.NewRootCommand(app, data) + healthcheckCreate := healthcheck.NewCreateCommand(healthcheckCmdRoot.CmdClause, data) + healthcheckDelete := healthcheck.NewDeleteCommand(healthcheckCmdRoot.CmdClause, data) + healthcheckDescribe := healthcheck.NewDescribeCommand(healthcheckCmdRoot.CmdClause, data) + healthcheckList := healthcheck.NewListCommand(healthcheckCmdRoot.CmdClause, data) + healthcheckUpdate := healthcheck.NewUpdateCommand(healthcheckCmdRoot.CmdClause, data) + ipCmdRoot := ip.NewRootCommand(app, data) + kvstoreCmdRoot := kvstore.NewRootCommand(app, data) + kvstoreCreate := kvstore.NewCreateCommand(kvstoreCmdRoot.CmdClause, data) + kvstoreDelete := kvstore.NewDeleteCommand(kvstoreCmdRoot.CmdClause, data) + kvstoreDescribe := kvstore.NewDescribeCommand(kvstoreCmdRoot.CmdClause, data) + kvstoreList := kvstore.NewListCommand(kvstoreCmdRoot.CmdClause, data) + kvstoreentryCmdRoot := kvstoreentry.NewRootCommand(app, data) + kvstoreentryCreate := kvstoreentry.NewCreateCommand(kvstoreentryCmdRoot.CmdClause, data) + kvstoreentryDelete := kvstoreentry.NewDeleteCommand(kvstoreentryCmdRoot.CmdClause, data) + kvstoreentryDescribe := kvstoreentry.NewDescribeCommand(kvstoreentryCmdRoot.CmdClause, data) + kvstoreentryList := kvstoreentry.NewListCommand(kvstoreentryCmdRoot.CmdClause, data) + logtailCmdRoot := logtail.NewRootCommand(app, data) + loggingCmdRoot := logging.NewRootCommand(app, data) + loggingAzureblobCmdRoot := azureblob.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingAzureblobCreate := azureblob.NewCreateCommand(loggingAzureblobCmdRoot.CmdClause, data) + loggingAzureblobDelete := azureblob.NewDeleteCommand(loggingAzureblobCmdRoot.CmdClause, data) + loggingAzureblobDescribe := azureblob.NewDescribeCommand(loggingAzureblobCmdRoot.CmdClause, data) + loggingAzureblobList := azureblob.NewListCommand(loggingAzureblobCmdRoot.CmdClause, data) + loggingAzureblobUpdate := azureblob.NewUpdateCommand(loggingAzureblobCmdRoot.CmdClause, data) + loggingBigQueryCmdRoot := bigquery.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingBigQueryCreate := bigquery.NewCreateCommand(loggingBigQueryCmdRoot.CmdClause, data) + loggingBigQueryDelete := bigquery.NewDeleteCommand(loggingBigQueryCmdRoot.CmdClause, data) + loggingBigQueryDescribe := bigquery.NewDescribeCommand(loggingBigQueryCmdRoot.CmdClause, data) + loggingBigQueryList := bigquery.NewListCommand(loggingBigQueryCmdRoot.CmdClause, data) + loggingBigQueryUpdate := bigquery.NewUpdateCommand(loggingBigQueryCmdRoot.CmdClause, data) + loggingCloudfilesCmdRoot := cloudfiles.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingCloudfilesCreate := cloudfiles.NewCreateCommand(loggingCloudfilesCmdRoot.CmdClause, data) + loggingCloudfilesDelete := cloudfiles.NewDeleteCommand(loggingCloudfilesCmdRoot.CmdClause, data) + loggingCloudfilesDescribe := cloudfiles.NewDescribeCommand(loggingCloudfilesCmdRoot.CmdClause, data) + loggingCloudfilesList := cloudfiles.NewListCommand(loggingCloudfilesCmdRoot.CmdClause, data) + loggingCloudfilesUpdate := cloudfiles.NewUpdateCommand(loggingCloudfilesCmdRoot.CmdClause, data) + loggingDatadogCmdRoot := datadog.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingDatadogCreate := datadog.NewCreateCommand(loggingDatadogCmdRoot.CmdClause, data) + loggingDatadogDelete := datadog.NewDeleteCommand(loggingDatadogCmdRoot.CmdClause, data) + loggingDatadogDescribe := datadog.NewDescribeCommand(loggingDatadogCmdRoot.CmdClause, data) + loggingDatadogList := datadog.NewListCommand(loggingDatadogCmdRoot.CmdClause, data) + loggingDatadogUpdate := datadog.NewUpdateCommand(loggingDatadogCmdRoot.CmdClause, data) + loggingDigitaloceanCmdRoot := digitalocean.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingDigitaloceanCreate := digitalocean.NewCreateCommand(loggingDigitaloceanCmdRoot.CmdClause, data) + loggingDigitaloceanDelete := digitalocean.NewDeleteCommand(loggingDigitaloceanCmdRoot.CmdClause, data) + loggingDigitaloceanDescribe := digitalocean.NewDescribeCommand(loggingDigitaloceanCmdRoot.CmdClause, data) + loggingDigitaloceanList := digitalocean.NewListCommand(loggingDigitaloceanCmdRoot.CmdClause, data) + loggingDigitaloceanUpdate := digitalocean.NewUpdateCommand(loggingDigitaloceanCmdRoot.CmdClause, data) + loggingElasticsearchCmdRoot := elasticsearch.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingElasticsearchCreate := elasticsearch.NewCreateCommand(loggingElasticsearchCmdRoot.CmdClause, data) + loggingElasticsearchDelete := elasticsearch.NewDeleteCommand(loggingElasticsearchCmdRoot.CmdClause, data) + loggingElasticsearchDescribe := elasticsearch.NewDescribeCommand(loggingElasticsearchCmdRoot.CmdClause, data) + loggingElasticsearchList := elasticsearch.NewListCommand(loggingElasticsearchCmdRoot.CmdClause, data) + loggingElasticsearchUpdate := elasticsearch.NewUpdateCommand(loggingElasticsearchCmdRoot.CmdClause, data) + loggingFtpCmdRoot := ftp.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingFtpCreate := ftp.NewCreateCommand(loggingFtpCmdRoot.CmdClause, data) + loggingFtpDelete := ftp.NewDeleteCommand(loggingFtpCmdRoot.CmdClause, data) + loggingFtpDescribe := ftp.NewDescribeCommand(loggingFtpCmdRoot.CmdClause, data) + loggingFtpList := ftp.NewListCommand(loggingFtpCmdRoot.CmdClause, data) + loggingFtpUpdate := ftp.NewUpdateCommand(loggingFtpCmdRoot.CmdClause, data) + loggingGcsCmdRoot := gcs.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingGcsCreate := gcs.NewCreateCommand(loggingGcsCmdRoot.CmdClause, data) + loggingGcsDelete := gcs.NewDeleteCommand(loggingGcsCmdRoot.CmdClause, data) + loggingGcsDescribe := gcs.NewDescribeCommand(loggingGcsCmdRoot.CmdClause, data) + loggingGcsList := gcs.NewListCommand(loggingGcsCmdRoot.CmdClause, data) + loggingGcsUpdate := gcs.NewUpdateCommand(loggingGcsCmdRoot.CmdClause, data) + loggingGooglepubsubCmdRoot := googlepubsub.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingGooglepubsubCreate := googlepubsub.NewCreateCommand(loggingGooglepubsubCmdRoot.CmdClause, data) + loggingGooglepubsubDelete := googlepubsub.NewDeleteCommand(loggingGooglepubsubCmdRoot.CmdClause, data) + loggingGooglepubsubDescribe := googlepubsub.NewDescribeCommand(loggingGooglepubsubCmdRoot.CmdClause, data) + loggingGooglepubsubList := googlepubsub.NewListCommand(loggingGooglepubsubCmdRoot.CmdClause, data) + loggingGooglepubsubUpdate := googlepubsub.NewUpdateCommand(loggingGooglepubsubCmdRoot.CmdClause, data) + loggingHerokuCmdRoot := heroku.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingHerokuCreate := heroku.NewCreateCommand(loggingHerokuCmdRoot.CmdClause, data) + loggingHerokuDelete := heroku.NewDeleteCommand(loggingHerokuCmdRoot.CmdClause, data) + loggingHerokuDescribe := heroku.NewDescribeCommand(loggingHerokuCmdRoot.CmdClause, data) + loggingHerokuList := heroku.NewListCommand(loggingHerokuCmdRoot.CmdClause, data) + loggingHerokuUpdate := heroku.NewUpdateCommand(loggingHerokuCmdRoot.CmdClause, data) + loggingHoneycombCmdRoot := honeycomb.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingHoneycombCreate := honeycomb.NewCreateCommand(loggingHoneycombCmdRoot.CmdClause, data) + loggingHoneycombDelete := honeycomb.NewDeleteCommand(loggingHoneycombCmdRoot.CmdClause, data) + loggingHoneycombDescribe := honeycomb.NewDescribeCommand(loggingHoneycombCmdRoot.CmdClause, data) + loggingHoneycombList := honeycomb.NewListCommand(loggingHoneycombCmdRoot.CmdClause, data) + loggingHoneycombUpdate := honeycomb.NewUpdateCommand(loggingHoneycombCmdRoot.CmdClause, data) + loggingHTTPSCmdRoot := https.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingHTTPSCreate := https.NewCreateCommand(loggingHTTPSCmdRoot.CmdClause, data) + loggingHTTPSDelete := https.NewDeleteCommand(loggingHTTPSCmdRoot.CmdClause, data) + loggingHTTPSDescribe := https.NewDescribeCommand(loggingHTTPSCmdRoot.CmdClause, data) + loggingHTTPSList := https.NewListCommand(loggingHTTPSCmdRoot.CmdClause, data) + loggingHTTPSUpdate := https.NewUpdateCommand(loggingHTTPSCmdRoot.CmdClause, data) + loggingKafkaCmdRoot := kafka.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingKafkaCreate := kafka.NewCreateCommand(loggingKafkaCmdRoot.CmdClause, data) + loggingKafkaDelete := kafka.NewDeleteCommand(loggingKafkaCmdRoot.CmdClause, data) + loggingKafkaDescribe := kafka.NewDescribeCommand(loggingKafkaCmdRoot.CmdClause, data) + loggingKafkaList := kafka.NewListCommand(loggingKafkaCmdRoot.CmdClause, data) + loggingKafkaUpdate := kafka.NewUpdateCommand(loggingKafkaCmdRoot.CmdClause, data) + loggingKinesisCmdRoot := kinesis.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingKinesisCreate := kinesis.NewCreateCommand(loggingKinesisCmdRoot.CmdClause, data) + loggingKinesisDelete := kinesis.NewDeleteCommand(loggingKinesisCmdRoot.CmdClause, data) + loggingKinesisDescribe := kinesis.NewDescribeCommand(loggingKinesisCmdRoot.CmdClause, data) + loggingKinesisList := kinesis.NewListCommand(loggingKinesisCmdRoot.CmdClause, data) + loggingKinesisUpdate := kinesis.NewUpdateCommand(loggingKinesisCmdRoot.CmdClause, data) + loggingLogglyCmdRoot := loggly.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingLogglyCreate := loggly.NewCreateCommand(loggingLogglyCmdRoot.CmdClause, data) + loggingLogglyDelete := loggly.NewDeleteCommand(loggingLogglyCmdRoot.CmdClause, data) + loggingLogglyDescribe := loggly.NewDescribeCommand(loggingLogglyCmdRoot.CmdClause, data) + loggingLogglyList := loggly.NewListCommand(loggingLogglyCmdRoot.CmdClause, data) + loggingLogglyUpdate := loggly.NewUpdateCommand(loggingLogglyCmdRoot.CmdClause, data) + loggingLogshuttleCmdRoot := logshuttle.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingLogshuttleCreate := logshuttle.NewCreateCommand(loggingLogshuttleCmdRoot.CmdClause, data) + loggingLogshuttleDelete := logshuttle.NewDeleteCommand(loggingLogshuttleCmdRoot.CmdClause, data) + loggingLogshuttleDescribe := logshuttle.NewDescribeCommand(loggingLogshuttleCmdRoot.CmdClause, data) + loggingLogshuttleList := logshuttle.NewListCommand(loggingLogshuttleCmdRoot.CmdClause, data) + loggingLogshuttleUpdate := logshuttle.NewUpdateCommand(loggingLogshuttleCmdRoot.CmdClause, data) + loggingNewRelicCmdRoot := newrelic.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingNewRelicCreate := newrelic.NewCreateCommand(loggingNewRelicCmdRoot.CmdClause, data) + loggingNewRelicDelete := newrelic.NewDeleteCommand(loggingNewRelicCmdRoot.CmdClause, data) + loggingNewRelicDescribe := newrelic.NewDescribeCommand(loggingNewRelicCmdRoot.CmdClause, data) + loggingNewRelicList := newrelic.NewListCommand(loggingNewRelicCmdRoot.CmdClause, data) + loggingNewRelicUpdate := newrelic.NewUpdateCommand(loggingNewRelicCmdRoot.CmdClause, data) + loggingNewRelicOTLPCmdRoot := newrelicotlp.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingNewRelicOTLPCreate := newrelicotlp.NewCreateCommand(loggingNewRelicOTLPCmdRoot.CmdClause, data) + loggingNewRelicOTLPDelete := newrelicotlp.NewDeleteCommand(loggingNewRelicOTLPCmdRoot.CmdClause, data) + loggingNewRelicOTLPDescribe := newrelicotlp.NewDescribeCommand(loggingNewRelicOTLPCmdRoot.CmdClause, data) + loggingNewRelicOTLPList := newrelicotlp.NewListCommand(loggingNewRelicOTLPCmdRoot.CmdClause, data) + loggingNewRelicOTLPUpdate := newrelicotlp.NewUpdateCommand(loggingNewRelicOTLPCmdRoot.CmdClause, data) + loggingOpenstackCmdRoot := openstack.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingOpenstackCreate := openstack.NewCreateCommand(loggingOpenstackCmdRoot.CmdClause, data) + loggingOpenstackDelete := openstack.NewDeleteCommand(loggingOpenstackCmdRoot.CmdClause, data) + loggingOpenstackDescribe := openstack.NewDescribeCommand(loggingOpenstackCmdRoot.CmdClause, data) + loggingOpenstackList := openstack.NewListCommand(loggingOpenstackCmdRoot.CmdClause, data) + loggingOpenstackUpdate := openstack.NewUpdateCommand(loggingOpenstackCmdRoot.CmdClause, data) + loggingPapertrailCmdRoot := papertrail.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingPapertrailCreate := papertrail.NewCreateCommand(loggingPapertrailCmdRoot.CmdClause, data) + loggingPapertrailDelete := papertrail.NewDeleteCommand(loggingPapertrailCmdRoot.CmdClause, data) + loggingPapertrailDescribe := papertrail.NewDescribeCommand(loggingPapertrailCmdRoot.CmdClause, data) + loggingPapertrailList := papertrail.NewListCommand(loggingPapertrailCmdRoot.CmdClause, data) + loggingPapertrailUpdate := papertrail.NewUpdateCommand(loggingPapertrailCmdRoot.CmdClause, data) + loggingS3CmdRoot := s3.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingS3Create := s3.NewCreateCommand(loggingS3CmdRoot.CmdClause, data) + loggingS3Delete := s3.NewDeleteCommand(loggingS3CmdRoot.CmdClause, data) + loggingS3Describe := s3.NewDescribeCommand(loggingS3CmdRoot.CmdClause, data) + loggingS3List := s3.NewListCommand(loggingS3CmdRoot.CmdClause, data) + loggingS3Update := s3.NewUpdateCommand(loggingS3CmdRoot.CmdClause, data) + loggingScalyrCmdRoot := scalyr.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingScalyrCreate := scalyr.NewCreateCommand(loggingScalyrCmdRoot.CmdClause, data) + loggingScalyrDelete := scalyr.NewDeleteCommand(loggingScalyrCmdRoot.CmdClause, data) + loggingScalyrDescribe := scalyr.NewDescribeCommand(loggingScalyrCmdRoot.CmdClause, data) + loggingScalyrList := scalyr.NewListCommand(loggingScalyrCmdRoot.CmdClause, data) + loggingScalyrUpdate := scalyr.NewUpdateCommand(loggingScalyrCmdRoot.CmdClause, data) + loggingSftpCmdRoot := sftp.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingSftpCreate := sftp.NewCreateCommand(loggingSftpCmdRoot.CmdClause, data) + loggingSftpDelete := sftp.NewDeleteCommand(loggingSftpCmdRoot.CmdClause, data) + loggingSftpDescribe := sftp.NewDescribeCommand(loggingSftpCmdRoot.CmdClause, data) + loggingSftpList := sftp.NewListCommand(loggingSftpCmdRoot.CmdClause, data) + loggingSftpUpdate := sftp.NewUpdateCommand(loggingSftpCmdRoot.CmdClause, data) + loggingSplunkCmdRoot := splunk.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingSplunkCreate := splunk.NewCreateCommand(loggingSplunkCmdRoot.CmdClause, data) + loggingSplunkDelete := splunk.NewDeleteCommand(loggingSplunkCmdRoot.CmdClause, data) + loggingSplunkDescribe := splunk.NewDescribeCommand(loggingSplunkCmdRoot.CmdClause, data) + loggingSplunkList := splunk.NewListCommand(loggingSplunkCmdRoot.CmdClause, data) + loggingSplunkUpdate := splunk.NewUpdateCommand(loggingSplunkCmdRoot.CmdClause, data) + loggingSumologicCmdRoot := sumologic.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingSumologicCreate := sumologic.NewCreateCommand(loggingSumologicCmdRoot.CmdClause, data) + loggingSumologicDelete := sumologic.NewDeleteCommand(loggingSumologicCmdRoot.CmdClause, data) + loggingSumologicDescribe := sumologic.NewDescribeCommand(loggingSumologicCmdRoot.CmdClause, data) + loggingSumologicList := sumologic.NewListCommand(loggingSumologicCmdRoot.CmdClause, data) + loggingSumologicUpdate := sumologic.NewUpdateCommand(loggingSumologicCmdRoot.CmdClause, data) + loggingSyslogCmdRoot := syslog.NewRootCommand(loggingCmdRoot.CmdClause, data) + loggingSyslogCreate := syslog.NewCreateCommand(loggingSyslogCmdRoot.CmdClause, data) + loggingSyslogDelete := syslog.NewDeleteCommand(loggingSyslogCmdRoot.CmdClause, data) + loggingSyslogDescribe := syslog.NewDescribeCommand(loggingSyslogCmdRoot.CmdClause, data) + loggingSyslogList := syslog.NewListCommand(loggingSyslogCmdRoot.CmdClause, data) + loggingSyslogUpdate := syslog.NewUpdateCommand(loggingSyslogCmdRoot.CmdClause, data) + popCmdRoot := pop.NewRootCommand(app, data) + productsCmdRoot := products.NewRootCommand(app, data) + profileCmdRoot := profile.NewRootCommand(app, data) + profileCreate := profile.NewCreateCommand(profileCmdRoot.CmdClause, data, ssoCmdRoot) + profileDelete := profile.NewDeleteCommand(profileCmdRoot.CmdClause, data) + profileList := profile.NewListCommand(profileCmdRoot.CmdClause, data) + profileSwitch := profile.NewSwitchCommand(profileCmdRoot.CmdClause, data) + profileToken := profile.NewTokenCommand(profileCmdRoot.CmdClause, data) + profileUpdate := profile.NewUpdateCommand(profileCmdRoot.CmdClause, data, ssoCmdRoot) + purgeCmdRoot := purge.NewRootCommand(app, data) + rateLimitCmdRoot := ratelimit.NewRootCommand(app, data) + rateLimitCreate := ratelimit.NewCreateCommand(rateLimitCmdRoot.CmdClause, data) + rateLimitDelete := ratelimit.NewDeleteCommand(rateLimitCmdRoot.CmdClause, data) + rateLimitDescribe := ratelimit.NewDescribeCommand(rateLimitCmdRoot.CmdClause, data) + rateLimitList := ratelimit.NewListCommand(rateLimitCmdRoot.CmdClause, data) + rateLimitUpdate := ratelimit.NewUpdateCommand(rateLimitCmdRoot.CmdClause, data) + resourcelinkCmdRoot := resourcelink.NewRootCommand(app, data) + resourcelinkCreate := resourcelink.NewCreateCommand(resourcelinkCmdRoot.CmdClause, data) + resourcelinkDelete := resourcelink.NewDeleteCommand(resourcelinkCmdRoot.CmdClause, data) + resourcelinkDescribe := resourcelink.NewDescribeCommand(resourcelinkCmdRoot.CmdClause, data) + resourcelinkList := resourcelink.NewListCommand(resourcelinkCmdRoot.CmdClause, data) + resourcelinkUpdate := resourcelink.NewUpdateCommand(resourcelinkCmdRoot.CmdClause, data) + secretstoreCmdRoot := secretstore.NewRootCommand(app, data) + secretstoreCreate := secretstore.NewCreateCommand(secretstoreCmdRoot.CmdClause, data) + secretstoreDescribe := secretstore.NewDescribeCommand(secretstoreCmdRoot.CmdClause, data) + secretstoreDelete := secretstore.NewDeleteCommand(secretstoreCmdRoot.CmdClause, data) + secretstoreList := secretstore.NewListCommand(secretstoreCmdRoot.CmdClause, data) + secretstoreentryCmdRoot := secretstoreentry.NewRootCommand(app, data) + secretstoreentryCreate := secretstoreentry.NewCreateCommand(secretstoreentryCmdRoot.CmdClause, data) + secretstoreentryDescribe := secretstoreentry.NewDescribeCommand(secretstoreentryCmdRoot.CmdClause, data) + secretstoreentryDelete := secretstoreentry.NewDeleteCommand(secretstoreentryCmdRoot.CmdClause, data) + secretstoreentryList := secretstoreentry.NewListCommand(secretstoreentryCmdRoot.CmdClause, data) + serviceCmdRoot := service.NewRootCommand(app, data) + serviceCreate := service.NewCreateCommand(serviceCmdRoot.CmdClause, data) + serviceDelete := service.NewDeleteCommand(serviceCmdRoot.CmdClause, data) + serviceDescribe := service.NewDescribeCommand(serviceCmdRoot.CmdClause, data) + serviceList := service.NewListCommand(serviceCmdRoot.CmdClause, data) + serviceSearch := service.NewSearchCommand(serviceCmdRoot.CmdClause, data) + serviceUpdate := service.NewUpdateCommand(serviceCmdRoot.CmdClause, data) + serviceauthCmdRoot := serviceauth.NewRootCommand(app, data) + serviceauthCreate := serviceauth.NewCreateCommand(serviceauthCmdRoot.CmdClause, data) + serviceauthDelete := serviceauth.NewDeleteCommand(serviceauthCmdRoot.CmdClause, data) + serviceauthDescribe := serviceauth.NewDescribeCommand(serviceauthCmdRoot.CmdClause, data) + serviceauthList := serviceauth.NewListCommand(serviceauthCmdRoot.CmdClause, data) + serviceauthUpdate := serviceauth.NewUpdateCommand(serviceauthCmdRoot.CmdClause, data) + serviceVersionCmdRoot := serviceversion.NewRootCommand(app, data) + serviceVersionActivate := serviceversion.NewActivateCommand(serviceVersionCmdRoot.CmdClause, data) + serviceVersionClone := serviceversion.NewCloneCommand(serviceVersionCmdRoot.CmdClause, data) + serviceVersionDeactivate := serviceversion.NewDeactivateCommand(serviceVersionCmdRoot.CmdClause, data) + serviceVersionList := serviceversion.NewListCommand(serviceVersionCmdRoot.CmdClause, data) + serviceVersionLock := serviceversion.NewLockCommand(serviceVersionCmdRoot.CmdClause, data) + serviceVersionUpdate := serviceversion.NewUpdateCommand(serviceVersionCmdRoot.CmdClause, data) + statsCmdRoot := stats.NewRootCommand(app, data) + statsHistorical := stats.NewHistoricalCommand(statsCmdRoot.CmdClause, data) + statsRealtime := stats.NewRealtimeCommand(statsCmdRoot.CmdClause, data) + statsRegions := stats.NewRegionsCommand(statsCmdRoot.CmdClause, data) + tlsConfigCmdRoot := tlsconfig.NewRootCommand(app, data) + tlsConfigDescribe := tlsconfig.NewDescribeCommand(tlsConfigCmdRoot.CmdClause, data) + tlsConfigList := tlsconfig.NewListCommand(tlsConfigCmdRoot.CmdClause, data) + tlsConfigUpdate := tlsconfig.NewUpdateCommand(tlsConfigCmdRoot.CmdClause, data) + tlsCustomCmdRoot := tlscustom.NewRootCommand(app, data) + tlsCustomActivationCmdRoot := tlscustomactivation.NewRootCommand(tlsCustomCmdRoot.CmdClause, data) + tlsCustomActivationCreate := tlscustomactivation.NewCreateCommand(tlsCustomActivationCmdRoot.CmdClause, data) + tlsCustomActivationDelete := tlscustomactivation.NewDeleteCommand(tlsCustomActivationCmdRoot.CmdClause, data) + tlsCustomActivationDescribe := tlscustomactivation.NewDescribeCommand(tlsCustomActivationCmdRoot.CmdClause, data) + tlsCustomActivationList := tlscustomactivation.NewListCommand(tlsCustomActivationCmdRoot.CmdClause, data) + tlsCustomActivationUpdate := tlscustomactivation.NewUpdateCommand(tlsCustomActivationCmdRoot.CmdClause, data) + tlsCustomCertificateCmdRoot := tlscustomcertificate.NewRootCommand(tlsCustomCmdRoot.CmdClause, data) + tlsCustomCertificateCreate := tlscustomcertificate.NewCreateCommand(tlsCustomCertificateCmdRoot.CmdClause, data) + tlsCustomCertificateDelete := tlscustomcertificate.NewDeleteCommand(tlsCustomCertificateCmdRoot.CmdClause, data) + tlsCustomCertificateDescribe := tlscustomcertificate.NewDescribeCommand(tlsCustomCertificateCmdRoot.CmdClause, data) + tlsCustomCertificateList := tlscustomcertificate.NewListCommand(tlsCustomCertificateCmdRoot.CmdClause, data) + tlsCustomCertificateUpdate := tlscustomcertificate.NewUpdateCommand(tlsCustomCertificateCmdRoot.CmdClause, data) + tlsCustomDomainCmdRoot := tlscustomdomain.NewRootCommand(tlsCustomCmdRoot.CmdClause, data) + tlsCustomDomainList := tlscustomdomain.NewListCommand(tlsCustomDomainCmdRoot.CmdClause, data) + tlsCustomPrivateKeyCmdRoot := tlscustomprivatekey.NewRootCommand(tlsCustomCmdRoot.CmdClause, data) + tlsCustomPrivateKeyCreate := tlscustomprivatekey.NewCreateCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, data) + tlsCustomPrivateKeyDelete := tlscustomprivatekey.NewDeleteCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, data) + tlsCustomPrivateKeyDescribe := tlscustomprivatekey.NewDescribeCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, data) + tlsCustomPrivateKeyList := tlscustomprivatekey.NewListCommand(tlsCustomPrivateKeyCmdRoot.CmdClause, data) + tlsPlatformCmdRoot := tlsplatform.NewRootCommand(app, data) + tlsPlatformCreate := tlsplatform.NewCreateCommand(tlsPlatformCmdRoot.CmdClause, data) + tlsPlatformDelete := tlsplatform.NewDeleteCommand(tlsPlatformCmdRoot.CmdClause, data) + tlsPlatformDescribe := tlsplatform.NewDescribeCommand(tlsPlatformCmdRoot.CmdClause, data) + tlsPlatformList := tlsplatform.NewListCommand(tlsPlatformCmdRoot.CmdClause, data) + tlsPlatformUpdate := tlsplatform.NewUpdateCommand(tlsPlatformCmdRoot.CmdClause, data) + tlsSubscriptionCmdRoot := tlssubscription.NewRootCommand(app, data) + tlsSubscriptionCreate := tlssubscription.NewCreateCommand(tlsSubscriptionCmdRoot.CmdClause, data) + tlsSubscriptionDelete := tlssubscription.NewDeleteCommand(tlsSubscriptionCmdRoot.CmdClause, data) + tlsSubscriptionDescribe := tlssubscription.NewDescribeCommand(tlsSubscriptionCmdRoot.CmdClause, data) + tlsSubscriptionList := tlssubscription.NewListCommand(tlsSubscriptionCmdRoot.CmdClause, data) + tlsSubscriptionUpdate := tlssubscription.NewUpdateCommand(tlsSubscriptionCmdRoot.CmdClause, data) + updateRoot := update.NewRootCommand(app, data) + userCmdRoot := user.NewRootCommand(app, data) + userCreate := user.NewCreateCommand(userCmdRoot.CmdClause, data) + userDelete := user.NewDeleteCommand(userCmdRoot.CmdClause, data) + userDescribe := user.NewDescribeCommand(userCmdRoot.CmdClause, data) + userList := user.NewListCommand(userCmdRoot.CmdClause, data) + userUpdate := user.NewUpdateCommand(userCmdRoot.CmdClause, data) + vclCmdRoot := vcl.NewRootCommand(app, data) + vclConditionCmdRoot := condition.NewRootCommand(vclCmdRoot.CmdClause, data) + vclConditionCreate := condition.NewCreateCommand(vclConditionCmdRoot.CmdClause, data) + vclConditionDelete := condition.NewDeleteCommand(vclConditionCmdRoot.CmdClause, data) + vclConditionDescribe := condition.NewDescribeCommand(vclConditionCmdRoot.CmdClause, data) + vclConditionList := condition.NewListCommand(vclConditionCmdRoot.CmdClause, data) + vclConditionUpdate := condition.NewUpdateCommand(vclConditionCmdRoot.CmdClause, data) + vclCustomCmdRoot := custom.NewRootCommand(vclCmdRoot.CmdClause, data) + vclCustomCreate := custom.NewCreateCommand(vclCustomCmdRoot.CmdClause, data) + vclCustomDelete := custom.NewDeleteCommand(vclCustomCmdRoot.CmdClause, data) + vclCustomDescribe := custom.NewDescribeCommand(vclCustomCmdRoot.CmdClause, data) + vclCustomList := custom.NewListCommand(vclCustomCmdRoot.CmdClause, data) + vclCustomUpdate := custom.NewUpdateCommand(vclCustomCmdRoot.CmdClause, data) + vclSnippetCmdRoot := snippet.NewRootCommand(vclCmdRoot.CmdClause, data) + vclSnippetCreate := snippet.NewCreateCommand(vclSnippetCmdRoot.CmdClause, data) + vclSnippetDelete := snippet.NewDeleteCommand(vclSnippetCmdRoot.CmdClause, data) + vclSnippetDescribe := snippet.NewDescribeCommand(vclSnippetCmdRoot.CmdClause, data) + vclSnippetList := snippet.NewListCommand(vclSnippetCmdRoot.CmdClause, data) + vclSnippetUpdate := snippet.NewUpdateCommand(vclSnippetCmdRoot.CmdClause, data) + versionCmdRoot := version.NewRootCommand(app, data) + whoamiCmdRoot := whoami.NewRootCommand(app, data) return []cmd.Command{ shellcompleteCmdRoot, diff --git a/pkg/commands/compute/build.go b/pkg/commands/compute/build.go index 4a4033324..f0684babc 100644 --- a/pkg/commands/compute/build.go +++ b/pkg/commands/compute/build.go @@ -57,7 +57,6 @@ type Flags struct { // BuildCommand produces a deployable artifact from files on the local disk. type BuildCommand struct { cmd.Base - wasmtoolsVersioner github.AssetVersioner // NOTE: Composite commands require these build flags to be public. // e.g. serve, publish, hashsum, hash-files @@ -69,11 +68,9 @@ type BuildCommand struct { } // NewBuildCommand returns a usable command registered under the parent. -func NewBuildCommand(parent cmd.Registerer, g *global.Data, wasmtoolsVersioner github.AssetVersioner) *BuildCommand { +func NewBuildCommand(parent cmd.Registerer, g *global.Data) *BuildCommand { var c BuildCommand c.Globals = g - c.wasmtoolsVersioner = wasmtoolsVersioner - c.CmdClause = parent.Command("build", "Build a Compute package locally") // NOTE: when updating these flags, be sure to update the composite commands: @@ -157,7 +154,7 @@ func (c *BuildCommand) Exec(in io.Reader, out io.Writer) (err error) { return err } - wasmtools, wasmtoolsErr := GetWasmTools(spinner, out, c.wasmtoolsVersioner, c.Globals) + wasmtools, wasmtoolsErr := GetWasmTools(spinner, out, c.Globals.Versioners.WasmTools, c.Globals) var pkgName string err = spinner.Process("Identifying package name", func(_ *text.SpinnerWrapper) error { diff --git a/pkg/commands/compute/build_test.go b/pkg/commands/compute/build_test.go index 11b59dc3b..8f4d70864 100644 --- a/pkg/commands/compute/build_test.go +++ b/pkg/commands/compute/build_test.go @@ -12,6 +12,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/compute" "github.com/fastly/cli/pkg/config" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" @@ -217,12 +218,12 @@ func TestBuildRust(t *testing.T) { }() var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) if testcase.applicationConfig != nil { - opts.ConfigFile = *testcase.applicationConfig + opts.Config = *testcase.applicationConfig } - opts.Versioners = app.Versioners{ + opts.Versioners = global.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", BinaryFilename: wasmtoolsBinName, @@ -410,12 +411,12 @@ func TestBuildGo(t *testing.T) { }() var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) if testcase.applicationConfig != nil { - opts.ConfigFile = *testcase.applicationConfig + opts.Config = *testcase.applicationConfig } - opts.Versioners = app.Versioners{ + opts.Versioners = global.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", BinaryFilename: wasmtoolsBinName, @@ -460,7 +461,7 @@ func TestBuildJavaScript(t *testing.T) { wantRemediationError string wantOutput []string npmInstall bool - versioners *app.Versioners + versioners *global.Versioners }{ { name: "no fastly.toml manifest", @@ -608,9 +609,9 @@ func TestBuildJavaScript(t *testing.T) { } var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.Versioners = app.Versioners{ + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) + opts.Versioners = global.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", BinaryFilename: wasmtoolsBinName, @@ -803,9 +804,9 @@ func TestBuildAssemblyScript(t *testing.T) { } var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.Versioners = app.Versioners{ + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) + opts.Versioners = global.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", BinaryFilename: wasmtoolsBinName, @@ -1015,10 +1016,10 @@ func TestBuildOther(t *testing.T) { } var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.Stdin = strings.NewReader(testcase.stdin) // NOTE: build only has one prompt when dealing with a custom build - opts.Versioners = app.Versioners{ + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) + opts.Input = strings.NewReader(testcase.stdin) // NOTE: build only has one prompt when dealing with a custom build + opts.Versioners = global.Versioners{ WasmTools: mock.AssetVersioner{ AssetVersion: "1.2.3", BinaryFilename: wasmtoolsBinName, diff --git a/pkg/commands/compute/compute_test.go b/pkg/commands/compute/compute_test.go index 2a6883ea8..4c77357e5 100644 --- a/pkg/commands/compute/compute_test.go +++ b/pkg/commands/compute/compute_test.go @@ -10,8 +10,8 @@ import ( "github.com/mholt/archiver/v3" "github.com/fastly/cli/pkg/commands/compute" - "github.com/fastly/cli/pkg/github" "github.com/fastly/cli/pkg/global" + "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/testutil" ) @@ -20,10 +20,11 @@ import ( // `compute build` and `compute deploy` commands from which publish is composed. func TestFlagDivergencePublish(t *testing.T) { var g global.Data + g.Manifest = &manifest.Data{} acmd := kingpin.New("foo", "bar") rcmd := compute.NewRootCommand(acmd, &g) - bcmd := compute.NewBuildCommand(rcmd.CmdClause, &g, nil) + bcmd := compute.NewBuildCommand(rcmd.CmdClause, &g) dcmd := compute.NewDeployCommand(rcmd.CmdClause, &g) pcmd := compute.NewPublishCommand(rcmd.CmdClause, &g, bcmd, dcmd) @@ -68,16 +69,11 @@ func TestFlagDivergencePublish(t *testing.T) { // `compute build` command as `compute serve` delegates to build. func TestFlagDivergenceServe(t *testing.T) { var cfg global.Data - versioner := github.New(github.Opts{ - Org: "fastly", - Repo: "viceroy", - Binary: "viceroy", - }) acmd := kingpin.New("foo", "bar") rcmd := compute.NewRootCommand(acmd, &cfg) - bcmd := compute.NewBuildCommand(rcmd.CmdClause, &cfg, nil) - scmd := compute.NewServeCommand(rcmd.CmdClause, &cfg, bcmd, versioner) + bcmd := compute.NewBuildCommand(rcmd.CmdClause, &cfg) + scmd := compute.NewServeCommand(rcmd.CmdClause, &cfg, bcmd) buildFlags := getFlags(bcmd.CmdClause) serveFlags := getFlags(scmd.CmdClause) @@ -135,7 +131,7 @@ func TestFlagDivergenceHashSum(t *testing.T) { acmd := kingpin.New("foo", "bar") rcmd := compute.NewRootCommand(acmd, &cfg) - bcmd := compute.NewBuildCommand(rcmd.CmdClause, &cfg, nil) + bcmd := compute.NewBuildCommand(rcmd.CmdClause, &cfg) hcmd := compute.NewHashsumCommand(rcmd.CmdClause, &cfg, bcmd) buildFlags := getFlags(bcmd.CmdClause) @@ -186,7 +182,7 @@ func TestFlagDivergenceHashFiles(t *testing.T) { acmd := kingpin.New("foo", "bar") rcmd := compute.NewRootCommand(acmd, &cfg) - bcmd := compute.NewBuildCommand(rcmd.CmdClause, &cfg, nil) + bcmd := compute.NewBuildCommand(rcmd.CmdClause, &cfg) hcmd := compute.NewHashFilesCommand(rcmd.CmdClause, &cfg, bcmd) buildFlags := getFlags(bcmd.CmdClause) diff --git a/pkg/commands/compute/deploy.go b/pkg/commands/compute/deploy.go index 2546e5ca9..811978738 100644 --- a/pkg/commands/compute/deploy.go +++ b/pkg/commands/compute/deploy.go @@ -147,7 +147,7 @@ func (c *DeployCommand) Exec(in io.Reader, out io.Writer) (err error) { } // Otherwise, we'll attempt to read the manifest from within the given // package archive. - if err := readManifestFromPackageArchive(&c.Globals.Manifest, c.PackagePath, manifestFilename); err != nil { + if err := readManifestFromPackageArchive(c.Globals.Manifest, c.PackagePath, manifestFilename); err != nil { return err } if c.Globals.Verbose() { @@ -358,7 +358,7 @@ func (c *DeployCommand) Setup(out io.Writer) (fnActivateTrial Activator, service // IMPORTANT: We don't handle the error when looking up the Service ID. // This is because later in the Exec() flow we might create a 'new' service. - serviceID, source, flag, err := cmd.ServiceID(c.ServiceName, c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.ServiceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err == nil && c.Globals.Verbose() { cmd.DisplayServiceID(serviceID, flag, source, out) } diff --git a/pkg/commands/compute/deploy_test.go b/pkg/commands/compute/deploy_test.go index 448410327..f098c7d49 100644 --- a/pkg/commands/compute/deploy_test.go +++ b/pkg/commands/compute/deploy_test.go @@ -17,6 +17,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/compute" "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" @@ -2153,7 +2154,7 @@ func TestDeploy(t *testing.T) { } var stdout threadsafe.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) if testcase.httpClientRes != nil || testcase.httpClientErr != nil { @@ -2172,7 +2173,7 @@ func TestDeploy(t *testing.T) { // To handle multiple prompt input from the user we need to do some // coordination around io pipes to mimic the required user behaviour. stdin, prompt := io.Pipe() - opts.Stdin = stdin + opts.Input = stdin // Wait for user input and write it to the prompt inputc := make(chan string) @@ -2187,7 +2188,7 @@ func TestDeploy(t *testing.T) { // Call `app.Run()` and wait for response go func() { - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.args, nil) @@ -2215,8 +2216,8 @@ func TestDeploy(t *testing.T) { if len(testcase.stdin) > 0 { stdin = testcase.stdin[0] } - opts.Stdin = strings.NewReader(stdin) - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.Input = strings.NewReader(stdin) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.args, nil) diff --git a/pkg/commands/compute/init.go b/pkg/commands/compute/init.go index 18b755f16..d508ed5cf 100644 --- a/pkg/commands/compute/init.go +++ b/pkg/commands/compute/init.go @@ -43,7 +43,6 @@ type InitCommand struct { dir string cloneFrom string language string - manifest manifest.Data tag string } @@ -51,13 +50,12 @@ type InitCommand struct { var Languages = []string{"rust", "javascript", "go", "other"} // NewInitCommand returns a usable command registered under the parent. -func NewInitCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *InitCommand { +func NewInitCommand(parent cmd.Registerer, g *global.Data) *InitCommand { var c InitCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("init", "Initialize a new Compute package locally") - c.CmdClause.Flag("author", "Author(s) of the package").Short('a').StringsVar(&c.manifest.File.Authors) + c.CmdClause.Flag("author", "Author(s) of the package").Short('a').StringsVar(&g.Manifest.File.Authors) c.CmdClause.Flag("branch", "Git branch name to clone from package template repository").Hidden().StringVar(&c.branch) c.CmdClause.Flag("directory", "Destination to write the new package, defaulting to the current directory").Short('p').StringVar(&c.dir) c.CmdClause.Flag("from", "Local project directory, or Git repository URL, or URL referencing a .zip/.tar.gz file, containing a package template").Short('f').StringVar(&c.cloneFrom) @@ -106,7 +104,7 @@ func (c *InitCommand) Exec(in io.Reader, out io.Writer) (err error) { return fmt.Errorf("error determining current directory: %w", err) } - mf := c.manifest.File + mf := c.Globals.Manifest.File if c.Globals.Flags.Quiet { mf.SetQuiet(true) } @@ -441,9 +439,9 @@ func validateDirectoryPermissions(dst string) text.SpinnerProcess { // returned as is. func (c *InitCommand) PromptOrReturn(email string, in io.Reader, out io.Writer) (name, description string, authors []string, err error) { flags := c.Globals.Flags - name, _ = c.manifest.Name() - description, _ = c.manifest.Description() - authors, _ = c.manifest.Authors() + name, _ = c.Globals.Manifest.Name() + description, _ = c.Globals.Manifest.Description() + authors, _ = c.Globals.Manifest.Authors() if name == "" && !flags.AcceptDefaults && !flags.NonInteractive { text.Break(out) diff --git a/pkg/commands/compute/init_test.go b/pkg/commands/compute/init_test.go index f427fb0f3..ecfc161d7 100644 --- a/pkg/commands/compute/init_test.go +++ b/pkg/commands/compute/init_test.go @@ -11,6 +11,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/config" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/testutil" ) @@ -359,14 +360,14 @@ func TestInit(t *testing.T) { }() var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) - opts.ConfigFile = testcase.configFile + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) + opts.Config = testcase.configFile // we need to define stdin as the init process prompts the user multiple // times, but we don't need to provide any values as all our prompts will // fallback to default values if the input is unrecognised. - opts.Stdin = strings.NewReader(testcase.stdin) + opts.Input = strings.NewReader(testcase.stdin) return opts, nil } err = app.Run(testcase.args, nil) diff --git a/pkg/commands/compute/metadata_test.go b/pkg/commands/compute/metadata_test.go index 1083469a9..0c756682b 100644 --- a/pkg/commands/compute/metadata_test.go +++ b/pkg/commands/compute/metadata_test.go @@ -12,6 +12,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/config" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/revision" "github.com/fastly/cli/pkg/testutil" ) @@ -124,7 +125,7 @@ func TestMetadata(t *testing.T) { t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) // We override the config path so that we don't accidentally write over // our own configuration file. @@ -141,14 +142,14 @@ func TestMetadata(t *testing.T) { // The read of the config file only happens in the main() function, so for // the sake of the test environment we need to construct an in-memory // representation of the config file we want to be using. - opts.ConfigFile = config.File{ + opts.Config = config.File{ ConfigVersion: staticConfig.ConfigVersion, CLI: config.CLI{ Version: revision.SemVer(revision.AppVersion), }, } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -163,19 +164,19 @@ func TestMetadata(t *testing.T) { in := strings.NewReader("") verboseMode := false - err = opts.ConfigFile.Read(configPath, in, opts.Stdout, opts.ErrLog, verboseMode) + err = opts.Config.Read(configPath, in, opts.Output, opts.ErrLog, verboseMode) if err != nil { t.Error(err) } - if opts.ConfigFile.WasmMetadata.BuildInfo != testcase.ExpectedConfig.BuildInfo { - t.Errorf("want: %s, got: %s", testcase.ExpectedConfig.BuildInfo, opts.ConfigFile.WasmMetadata.BuildInfo) + if opts.Config.WasmMetadata.BuildInfo != testcase.ExpectedConfig.BuildInfo { + t.Errorf("want: %s, got: %s", testcase.ExpectedConfig.BuildInfo, opts.Config.WasmMetadata.BuildInfo) } - if opts.ConfigFile.WasmMetadata.MachineInfo != testcase.ExpectedConfig.MachineInfo { - t.Errorf("want: %s, got: %s", testcase.ExpectedConfig.MachineInfo, opts.ConfigFile.WasmMetadata.MachineInfo) + if opts.Config.WasmMetadata.MachineInfo != testcase.ExpectedConfig.MachineInfo { + t.Errorf("want: %s, got: %s", testcase.ExpectedConfig.MachineInfo, opts.Config.WasmMetadata.MachineInfo) } - if opts.ConfigFile.WasmMetadata.PackageInfo != testcase.ExpectedConfig.PackageInfo { - t.Errorf("want: %s, got: %s", testcase.ExpectedConfig.PackageInfo, opts.ConfigFile.WasmMetadata.PackageInfo) + if opts.Config.WasmMetadata.PackageInfo != testcase.ExpectedConfig.PackageInfo { + t.Errorf("want: %s, got: %s", testcase.ExpectedConfig.PackageInfo, opts.Config.WasmMetadata.PackageInfo) } }) } diff --git a/pkg/commands/compute/pack.go b/pkg/commands/compute/pack.go index 3afbaff46..3d551257d 100644 --- a/pkg/commands/compute/pack.go +++ b/pkg/commands/compute/pack.go @@ -19,16 +19,13 @@ import ( // PackCommand takes a .wasm and builds the required tar/gzip package ready to be uploaded. type PackCommand struct { cmd.Base - manifest manifest.Data wasmBinary string } // NewPackCommand returns a usable command registered under the parent. -func NewPackCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *PackCommand { +func NewPackCommand(parent cmd.Registerer, g *global.Data) *PackCommand { var c PackCommand c.Globals = g - c.manifest = m - c.CmdClause = parent.Command("pack", "Package a pre-compiled Wasm binary for a Fastly Compute service") c.CmdClause.Flag("wasm-binary", "Path to a pre-compiled Wasm binary").Short('w').Required().StringVar(&c.wasmBinary) @@ -51,7 +48,7 @@ func (c *PackCommand) Exec(_ io.Reader, out io.Writer) (err error) { } }(c.Globals.ErrLog) - if err = c.manifest.File.ReadError(); err != nil { + if err = c.Globals.Manifest.File.ReadError(); err != nil { return err } diff --git a/pkg/commands/compute/pack_test.go b/pkg/commands/compute/pack_test.go index 1757c6aa3..905cd691e 100644 --- a/pkg/commands/compute/pack_test.go +++ b/pkg/commands/compute/pack_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/testutil" ) @@ -82,8 +83,8 @@ func TestPack(t *testing.T) { }() var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - return testutil.NewRunOpts(testcase.args, &stdout), nil + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + return testutil.MockGlobalData(testcase.args, &stdout), nil } err = app.Run(testcase.args, nil) diff --git a/pkg/commands/compute/serve.go b/pkg/commands/compute/serve.go index 9cc9045cf..f4563a30b 100644 --- a/pkg/commands/compute/serve.go +++ b/pkg/commands/compute/serve.go @@ -71,13 +71,11 @@ type ServeCommand struct { } // NewServeCommand returns a usable command registered under the parent. -func NewServeCommand(parent cmd.Registerer, g *global.Data, build *BuildCommand, viceroyVersioner github.AssetVersioner) *ServeCommand { +func NewServeCommand(parent cmd.Registerer, g *global.Data, build *BuildCommand) *ServeCommand { var c ServeCommand - c.build = build - c.ViceroyVersioner = viceroyVersioner - c.Globals = g + c.ViceroyVersioner = g.Versioners.Viceroy c.CmdClause = parent.Command("serve", "Build and run a Compute package locally") c.CmdClause.Flag("addr", "The IPv4 address and port to listen on").Default("127.0.0.1:7676").StringVar(&c.addr) diff --git a/pkg/commands/compute/update.go b/pkg/commands/compute/update.go index cf274c08d..99ddf21d3 100644 --- a/pkg/commands/compute/update.go +++ b/pkg/commands/compute/update.go @@ -18,7 +18,6 @@ import ( // UpdateCommand calls the Fastly API to update packages. type UpdateCommand struct { cmd.Base - manifest manifest.Data path string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -26,18 +25,17 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a package on a Fastly Compute service version") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) (err error) { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, @@ -81,7 +79,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) (err error) { packagePath := c.path if packagePath == "" { - projectName, source := c.manifest.Name() + projectName, source := c.Globals.Manifest.Name() if source == manifest.SourceUndefined { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to read project name: %w", fsterr.ErrReadingManifest), diff --git a/pkg/commands/compute/update_test.go b/pkg/commands/compute/update_test.go index dd5426995..85c6a659f 100644 --- a/pkg/commands/compute/update_test.go +++ b/pkg/commands/compute/update_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -76,8 +77,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/compute/validate_test.go b/pkg/commands/compute/validate_test.go index ff331dc0d..152768f44 100644 --- a/pkg/commands/compute/validate_test.go +++ b/pkg/commands/compute/validate_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/testutil" ) @@ -54,8 +55,8 @@ func TestValidate(t *testing.T) { }() var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - return testutil.NewRunOpts(testcase.Args, &stdout), nil + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + return testutil.MockGlobalData(testcase.Args, &stdout), nil } err = app.Run(testcase.Args, nil) testutil.AssertErrorContains(t, err, testcase.WantError) diff --git a/pkg/commands/config/config_test.go b/pkg/commands/config/config_test.go index 2afd73c30..caa14fa97 100644 --- a/pkg/commands/config/config_test.go +++ b/pkg/commands/config/config_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -71,8 +72,8 @@ func TestConfig(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) opts.ConfigPath = configPath return opts, nil diff --git a/pkg/commands/configstore/configstore_test.go b/pkg/commands/configstore/configstore_test.go index 4e79579f2..9c47d4abe 100644 --- a/pkg/commands/configstore/configstore_test.go +++ b/pkg/commands/configstore/configstore_test.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/configstore" fstfmt "github.com/fastly/cli/pkg/fmt" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -75,8 +76,8 @@ func TestCreateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -138,8 +139,8 @@ func TestDeleteStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -242,8 +243,8 @@ func TestDescribeStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -310,8 +311,8 @@ func TestListStoresCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -376,8 +377,8 @@ func TestListStoreServicesCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -447,8 +448,8 @@ func TestUpdateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/configstore/create.go b/pkg/commands/configstore/create.go index a4ba6d36e..c00e29052 100644 --- a/pkg/commands/configstore/create.go +++ b/pkg/commands/configstore/create.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a new config store") @@ -42,9 +40,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C type CreateCommand struct { cmd.Base cmd.JSONOutput - - input fastly.CreateConfigStoreInput - manifest manifest.Data + input fastly.CreateConfigStoreInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstore/delete.go b/pkg/commands/configstore/delete.go index af019dbe6..484ce746d 100644 --- a/pkg/commands/configstore/delete.go +++ b/pkg/commands/configstore/delete.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a config store") @@ -36,9 +34,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base cmd.JSONOutput - - input fastly.DeleteConfigStoreInput - manifest manifest.Data + input fastly.DeleteConfigStoreInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstore/describe.go b/pkg/commands/configstore/describe.go index ebb5935ab..82fa7d1bd 100644 --- a/pkg/commands/configstore/describe.go +++ b/pkg/commands/configstore/describe.go @@ -3,21 +3,20 @@ package configstore import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Retrieve a single config store").Alias("get") @@ -41,9 +40,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) type DescribeCommand struct { cmd.Base cmd.JSONOutput - input fastly.GetConfigStoreInput - manifest manifest.Data metadata bool } diff --git a/pkg/commands/configstore/list.go b/pkg/commands/configstore/list.go index 6dd3a3a48..03187f3ef 100644 --- a/pkg/commands/configstore/list.go +++ b/pkg/commands/configstore/list.go @@ -6,17 +6,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List config stores") @@ -31,8 +29,6 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis type ListCommand struct { cmd.Base cmd.JSONOutput - - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstore/list_services.go b/pkg/commands/configstore/list_services.go index 807b8e0cf..8031da294 100644 --- a/pkg/commands/configstore/list_services.go +++ b/pkg/commands/configstore/list_services.go @@ -3,21 +3,20 @@ package configstore import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListServicesCommand returns a usable command registered under the parent. -func NewListServicesCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListServicesCommand { +func NewListServicesCommand(parent cmd.Registerer, g *global.Data) *ListServicesCommand { c := ListServicesCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list-services", "List config store's services") @@ -35,9 +34,7 @@ func NewListServicesCommand(parent cmd.Registerer, g *global.Data, m manifest.Da type ListServicesCommand struct { cmd.Base cmd.JSONOutput - - input fastly.ListConfigStoreServicesInput - manifest manifest.Data + input fastly.ListConfigStoreServicesInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstore/update.go b/pkg/commands/configstore/update.go index 0e1409d52..bdbe5f444 100644 --- a/pkg/commands/configstore/update.go +++ b/pkg/commands/configstore/update.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a config store") @@ -43,9 +41,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U type UpdateCommand struct { cmd.Base cmd.JSONOutput - - input fastly.UpdateConfigStoreInput - manifest manifest.Data + input fastly.UpdateConfigStoreInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstoreentry/configstoreentry_test.go b/pkg/commands/configstoreentry/configstoreentry_test.go index cfdafca0f..693d1a954 100644 --- a/pkg/commands/configstoreentry/configstoreentry_test.go +++ b/pkg/commands/configstoreentry/configstoreentry_test.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/configstoreentry" fstfmt "github.com/fastly/cli/pkg/fmt" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" "github.com/fastly/cli/pkg/text" @@ -81,8 +82,8 @@ func TestCreateEntryCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -199,8 +200,8 @@ SUCCESS: Deleted all keys from Config Store '%s' testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -277,8 +278,8 @@ func TestDescribeEntryCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -343,8 +344,8 @@ func TestListEntriesCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -417,8 +418,8 @@ func TestUpdateEntryCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/configstoreentry/create.go b/pkg/commands/configstoreentry/create.go index 63d890457..65ffb3967 100644 --- a/pkg/commands/configstoreentry/create.go +++ b/pkg/commands/configstoreentry/create.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a new config store item").Alias("insert") @@ -57,10 +55,8 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C type CreateCommand struct { cmd.Base cmd.JSONOutput - - input fastly.CreateConfigStoreItemInput - stdin bool - manifest manifest.Data + input fastly.CreateConfigStoreItemInput + stdin bool } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstoreentry/delete.go b/pkg/commands/configstoreentry/delete.go index 8065c3ce9..dfd36cc7f 100644 --- a/pkg/commands/configstoreentry/delete.go +++ b/pkg/commands/configstoreentry/delete.go @@ -11,7 +11,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -24,12 +23,11 @@ const deleteKeysConcurrencyLimit int = 100 const batchLimit int = 100 // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a config store item") @@ -61,7 +59,6 @@ type DeleteCommand struct { concurrency cmd.OptionalInt deleteAll bool input fastly.DeleteConfigStoreItemInput - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstoreentry/describe.go b/pkg/commands/configstoreentry/describe.go index 6cc5a8db6..d55586b2f 100644 --- a/pkg/commands/configstoreentry/describe.go +++ b/pkg/commands/configstoreentry/describe.go @@ -3,21 +3,20 @@ package configstoreentry import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Retrieve a single config store item").Alias("get") @@ -42,9 +41,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) type DescribeCommand struct { cmd.Base cmd.JSONOutput - - input fastly.GetConfigStoreItemInput - manifest manifest.Data + input fastly.GetConfigStoreItemInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstoreentry/list.go b/pkg/commands/configstoreentry/list.go index 588ec9faf..1485ed698 100644 --- a/pkg/commands/configstoreentry/list.go +++ b/pkg/commands/configstoreentry/list.go @@ -3,21 +3,20 @@ package configstoreentry import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List config store items") @@ -35,9 +34,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis type ListCommand struct { cmd.Base cmd.JSONOutput - - input fastly.ListConfigStoreItemsInput - manifest manifest.Data + input fastly.ListConfigStoreItemsInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/configstoreentry/update.go b/pkg/commands/configstoreentry/update.go index 9ba313554..bd845e103 100644 --- a/pkg/commands/configstoreentry/update.go +++ b/pkg/commands/configstoreentry/update.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a config store item") @@ -63,10 +61,8 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U type UpdateCommand struct { cmd.Base cmd.JSONOutput - - input fastly.UpdateConfigStoreItemInput - stdin bool - manifest manifest.Data + input fastly.UpdateConfigStoreItemInput + stdin bool } // Exec invokes the application logic for the command. diff --git a/pkg/commands/dictionary/create.go b/pkg/commands/dictionary/create.go index ca098f826..7abd04191 100644 --- a/pkg/commands/dictionary/create.go +++ b/pkg/commands/dictionary/create.go @@ -8,14 +8,12 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // CreateCommand calls the Fastly API to create a service. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceVersion cmd.OptionalServiceVersion @@ -28,12 +26,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a Fastly edge dictionary on a Fastly service version") @@ -54,7 +51,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -72,7 +69,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/dictionary/delete.go b/pkg/commands/dictionary/delete.go index 04776f150..598d25f16 100644 --- a/pkg/commands/dictionary/delete.go +++ b/pkg/commands/dictionary/delete.go @@ -8,14 +8,12 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // DeleteCommand calls the Fastly API to delete a service. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteDictionaryInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -23,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Fastly edge dictionary from a Fastly service version") @@ -49,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -66,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/dictionary/describe.go b/pkg/commands/dictionary/describe.go index b6a3ca99e..dbd4a36a7 100644 --- a/pkg/commands/dictionary/describe.go +++ b/pkg/commands/dictionary/describe.go @@ -3,12 +3,12 @@ package dictionary import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a dictionary. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetDictionaryInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Fastly edge dictionary").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/dictionary/dictionary_test.go b/pkg/commands/dictionary/dictionary_test.go index 8157c900c..cd54e10a3 100644 --- a/pkg/commands/dictionary/dictionary_test.go +++ b/pkg/commands/dictionary/dictionary_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -57,8 +58,8 @@ func TestDictionaryDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -121,8 +122,8 @@ func TestDictionaryCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -168,8 +169,8 @@ func TestDeleteDictionary(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -213,8 +214,8 @@ func TestListDictionary(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -303,8 +304,8 @@ func TestUpdateDictionary(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/dictionary/list.go b/pkg/commands/dictionary/list.go index 2a345df16..65cada422 100644 --- a/pkg/commands/dictionary/list.go +++ b/pkg/commands/dictionary/list.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -18,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListDictionariesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List all dictionaries on a Fastly service version") @@ -47,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +64,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/dictionary/update.go b/pkg/commands/dictionary/update.go index f7cd41dc8..5e98948b2 100644 --- a/pkg/commands/dictionary/update.go +++ b/pkg/commands/dictionary/update.go @@ -10,14 +10,13 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // UpdateCommand calls the Fastly API to update a dictionary. type UpdateCommand struct { cmd.Base - manifest manifest.Data + // TODO: make input consistent across commands (most are title case) input fastly.UpdateDictionaryInput serviceName cmd.OptionalServiceNameID @@ -29,12 +28,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update name of dictionary on a Fastly service version").Alias("get") @@ -56,7 +54,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -74,7 +72,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/dictionaryentry/create.go b/pkg/commands/dictionaryentry/create.go index 82dcefda9..2f347a8e8 100644 --- a/pkg/commands/dictionaryentry/create.go +++ b/pkg/commands/dictionaryentry/create.go @@ -7,25 +7,22 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // CreateCommand calls the Fastly API to create a dictionary item. type CreateCommand struct { cmd.Base - manifest manifest.Data Input fastly.CreateDictionaryItemInput serviceName cmd.OptionalServiceNameID } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a new item on a Fastly edge dictionary") @@ -38,7 +35,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -52,7 +49,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/dictionaryentry/delete.go b/pkg/commands/dictionaryentry/delete.go index c78886306..7220e3815 100644 --- a/pkg/commands/dictionaryentry/delete.go +++ b/pkg/commands/dictionaryentry/delete.go @@ -7,25 +7,22 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // DeleteCommand calls the Fastly API to delete a service. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteDictionaryItemInput serviceName cmd.OptionalServiceNameID } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an item from a Fastly edge dictionary") @@ -37,7 +34,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -51,7 +48,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/dictionaryentry/describe.go b/pkg/commands/dictionaryentry/describe.go index a1afa9629..5bd265194 100644 --- a/pkg/commands/dictionaryentry/describe.go +++ b/pkg/commands/dictionaryentry/describe.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a dictionary item. @@ -17,18 +17,16 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetDictionaryItemInput serviceName cmd.OptionalServiceNameID } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Fastly edge dictionary item").Alias("get") @@ -41,7 +39,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -60,7 +58,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/dictionaryentry/dictionaryitem_test.go b/pkg/commands/dictionaryentry/dictionaryitem_test.go index 242a9f9d1..f2d2df4be 100644 --- a/pkg/commands/dictionaryentry/dictionaryitem_test.go +++ b/pkg/commands/dictionaryentry/dictionaryitem_test.go @@ -11,6 +11,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -48,8 +49,8 @@ func TestDictionaryItemDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -176,8 +177,8 @@ func TestDictionaryItemsList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -216,8 +217,8 @@ func TestDictionaryItemCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -295,8 +296,8 @@ func TestDictionaryItemUpdate(t *testing.T) { } var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -335,8 +336,8 @@ func TestDictionaryItemDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/dictionaryentry/list.go b/pkg/commands/dictionaryentry/list.go index c07149ad1..c581d116d 100644 --- a/pkg/commands/dictionaryentry/list.go +++ b/pkg/commands/dictionaryentry/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list dictionary items. @@ -17,18 +17,16 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data input fastly.ListDictionaryItemsInput serviceName cmd.OptionalServiceNameID } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List items in a Fastly edge dictionary") @@ -43,7 +41,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -62,7 +60,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/dictionaryentry/update.go b/pkg/commands/dictionaryentry/update.go index 8a13fc06c..ee50f9ffa 100644 --- a/pkg/commands/dictionaryentry/update.go +++ b/pkg/commands/dictionaryentry/update.go @@ -10,7 +10,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -21,17 +20,15 @@ type UpdateCommand struct { Input fastly.UpdateDictionaryItemInput InputBatch fastly.BatchModifyDictionaryItemsInput file cmd.OptionalString - manifest manifest.Data serviceName cmd.OptionalServiceNameID } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update or insert an item on a Fastly edge dictionary") @@ -44,7 +41,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -59,7 +56,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/doc.go b/pkg/commands/doc.go new file mode 100644 index 000000000..41caccb96 --- /dev/null +++ b/pkg/commands/doc.go @@ -0,0 +1,2 @@ +// Package commands contains functions for managing exposed CLI commands. +package commands diff --git a/pkg/commands/domain/create.go b/pkg/commands/domain/create.go index 733ed81cf..9d731aab5 100644 --- a/pkg/commands/domain/create.go +++ b/pkg/commands/domain/create.go @@ -3,18 +3,17 @@ package domain import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create domains. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceVersion cmd.OptionalServiceVersion @@ -27,12 +26,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a domain on a Fastly service version").Alias("add") @@ -54,7 +52,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -71,7 +69,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/domain/delete.go b/pkg/commands/domain/delete.go index 994f6a911..b41360af8 100644 --- a/pkg/commands/domain/delete.go +++ b/pkg/commands/domain/delete.go @@ -3,18 +3,17 @@ package domain import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete domains. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteDomainInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a domain on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/domain/describe.go b/pkg/commands/domain/describe.go index 863e64b24..0bb2c4c65 100644 --- a/pkg/commands/domain/describe.go +++ b/pkg/commands/domain/describe.go @@ -4,11 +4,11 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a domain. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetDomainInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a domain on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/domain/domain_test.go b/pkg/commands/domain/domain_test.go index 2e152d742..ea06f0e8d 100644 --- a/pkg/commands/domain/domain_test.go +++ b/pkg/commands/domain/domain_test.go @@ -11,6 +11,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestDomainCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -113,8 +114,8 @@ func TestDomainList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -153,8 +154,8 @@ func TestDomainDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -204,8 +205,8 @@ func TestDomainUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -246,8 +247,8 @@ func TestDomainDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -334,8 +335,8 @@ func TestDomainValidate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/domain/list.go b/pkg/commands/domain/list.go index d2495fe6f..96044799e 100644 --- a/pkg/commands/domain/list.go +++ b/pkg/commands/domain/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list domains. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListDomainsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List domains on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/domain/update.go b/pkg/commands/domain/update.go index 9421d7afc..6530c48bc 100644 --- a/pkg/commands/domain/update.go +++ b/pkg/commands/domain/update.go @@ -4,18 +4,17 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update domains. type UpdateCommand struct { cmd.Base - manifest manifest.Data input fastly.UpdateDomainInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -26,12 +25,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a domain on a Fastly service version") @@ -54,7 +52,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -71,7 +69,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/domain/validate.go b/pkg/commands/domain/validate.go index 25b088ab3..3aaeec5c8 100644 --- a/pkg/commands/domain/validate.go +++ b/pkg/commands/domain/validate.go @@ -9,15 +9,13 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) // NewValidateCommand returns a usable command registered under the parent. -func NewValidateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ValidateCommand { +func NewValidateCommand(parent cmd.Registerer, g *global.Data) *ValidateCommand { var c ValidateCommand c.CmdClause = parent.Command("validate", "Checks the status of a specific domain's DNS record for a Service Version") c.Globals = g - c.manifest = m // Required. c.RegisterFlag(cmd.StringFlagOpts{ @@ -33,7 +31,7 @@ func NewValidateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -51,7 +49,6 @@ type ValidateCommand struct { cmd.Base all bool - manifest manifest.Data name cmd.OptionalString serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -62,7 +59,7 @@ func (c *ValidateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/healthcheck/create.go b/pkg/commands/healthcheck/create.go index e1642e076..b2bd368ca 100644 --- a/pkg/commands/healthcheck/create.go +++ b/pkg/commands/healthcheck/create.go @@ -3,18 +3,17 @@ package healthcheck import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create healthchecks. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceVersion cmd.OptionalServiceVersion @@ -37,12 +36,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a healthcheck on a Fastly service version").Alias("add") @@ -71,7 +69,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -91,7 +89,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/healthcheck/delete.go b/pkg/commands/healthcheck/delete.go index 3fd8c6624..a003c1098 100644 --- a/pkg/commands/healthcheck/delete.go +++ b/pkg/commands/healthcheck/delete.go @@ -3,18 +3,17 @@ package healthcheck import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete healthchecks. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteHealthCheckInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a healthcheck on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/healthcheck/describe.go b/pkg/commands/healthcheck/describe.go index 72222f2d9..342854e7d 100644 --- a/pkg/commands/healthcheck/describe.go +++ b/pkg/commands/healthcheck/describe.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a healthcheck. @@ -17,19 +17,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetHealthCheckInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a healthcheck on a Fastly service version").Alias("get") @@ -47,7 +45,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -68,7 +66,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/healthcheck/healthcheck_test.go b/pkg/commands/healthcheck/healthcheck_test.go index 3f2fcaa2d..e9a4c838a 100644 --- a/pkg/commands/healthcheck/healthcheck_test.go +++ b/pkg/commands/healthcheck/healthcheck_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -51,8 +52,8 @@ func TestHealthCheckCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -124,8 +125,8 @@ func TestHealthCheckList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -169,8 +170,8 @@ func TestHealthCheckDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -224,8 +225,8 @@ func TestHealthCheckUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -271,8 +272,8 @@ func TestHealthCheckDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/healthcheck/list.go b/pkg/commands/healthcheck/list.go index fa910e371..5096056c4 100644 --- a/pkg/commands/healthcheck/list.go +++ b/pkg/commands/healthcheck/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list healthchecks. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListHealthChecksInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List healthchecks on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/healthcheck/update.go b/pkg/commands/healthcheck/update.go index ac7ca536e..700b77b53 100644 --- a/pkg/commands/healthcheck/update.go +++ b/pkg/commands/healthcheck/update.go @@ -3,18 +3,17 @@ package healthcheck import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update healthchecks. type UpdateCommand struct { cmd.Base - manifest manifest.Data input fastly.UpdateHealthCheckInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -35,12 +34,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a healthcheck on a Fastly service version") @@ -70,7 +68,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -90,7 +88,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/ip/ip_test.go b/pkg/commands/ip/ip_test.go index da5ff3bb7..f905a6bde 100644 --- a/pkg/commands/ip/ip_test.go +++ b/pkg/commands/ip/ip_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -24,8 +25,8 @@ func TestAllIPs(t *testing.T) { }, nil }, } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(args, &stdout) opts.APIClientFactory = mock.APIClient(api) return opts, nil } diff --git a/pkg/commands/kvstore/create.go b/pkg/commands/kvstore/create.go index c994f5d1d..a29252239 100644 --- a/pkg/commands/kvstore/create.go +++ b/pkg/commands/kvstore/create.go @@ -8,7 +8,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -17,17 +16,15 @@ type CreateCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data - Input fastly.CreateKVStoreInput + Input fastly.CreateKVStoreInput } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create an kv store") c.CmdClause.Flag("name", "Name of KV Store").Short('n').Required().StringVar(&c.Input.Name) diff --git a/pkg/commands/kvstore/delete.go b/pkg/commands/kvstore/delete.go index a379bb254..eaff97dfc 100644 --- a/pkg/commands/kvstore/delete.go +++ b/pkg/commands/kvstore/delete.go @@ -8,7 +8,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -17,17 +16,15 @@ type DeleteCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data - Input fastly.DeleteKVStoreInput + Input fastly.DeleteKVStoreInput } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an kv store") diff --git a/pkg/commands/kvstore/describe.go b/pkg/commands/kvstore/describe.go index ad0a26249..db83b0c2d 100644 --- a/pkg/commands/kvstore/describe.go +++ b/pkg/commands/kvstore/describe.go @@ -8,7 +8,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -17,17 +16,15 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data - Input fastly.GetKVStoreInput + Input fastly.GetKVStoreInput } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Describe an kv store").Alias("get") diff --git a/pkg/commands/kvstore/kvstore_test.go b/pkg/commands/kvstore/kvstore_test.go index fdfbc87c7..966c7bf35 100644 --- a/pkg/commands/kvstore/kvstore_test.go +++ b/pkg/commands/kvstore/kvstore_test.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/kvstore" fstfmt "github.com/fastly/cli/pkg/fmt" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" "github.com/fastly/cli/pkg/text" @@ -76,8 +77,8 @@ func TestCreateStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -139,8 +140,8 @@ func TestDeleteStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -217,8 +218,8 @@ func TestDescribeStoreCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -287,8 +288,8 @@ func TestListStoresCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/kvstore/list.go b/pkg/commands/kvstore/list.go index 659ece4eb..e69f3cce2 100644 --- a/pkg/commands/kvstore/list.go +++ b/pkg/commands/kvstore/list.go @@ -8,7 +8,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -16,17 +15,14 @@ import ( type ListCommand struct { cmd.Base cmd.JSONOutput - - manifest manifest.Data } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List kv stores") diff --git a/pkg/commands/kvstoreentry/create.go b/pkg/commands/kvstoreentry/create.go index f2a7c3ebb..72bdf05f8 100644 --- a/pkg/commands/kvstoreentry/create.go +++ b/pkg/commands/kvstoreentry/create.go @@ -17,18 +17,16 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/runtime" "github.com/fastly/cli/pkg/text" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Insert a key-value pair").Alias("insert") @@ -57,7 +55,6 @@ type CreateCommand struct { dirConcurrency int dirPath string filePath string - manifest manifest.Data stdin bool Input fastly.InsertKVStoreKeyInput diff --git a/pkg/commands/kvstoreentry/delete.go b/pkg/commands/kvstoreentry/delete.go index a8f5355e6..e5dd78bde 100644 --- a/pkg/commands/kvstoreentry/delete.go +++ b/pkg/commands/kvstoreentry/delete.go @@ -12,7 +12,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -28,17 +27,15 @@ type DeleteCommand struct { concurrency cmd.OptionalInt deleteAll bool key cmd.OptionalString - manifest manifest.Data storeID string } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a key") diff --git a/pkg/commands/kvstoreentry/describe.go b/pkg/commands/kvstoreentry/describe.go index b93fb574f..087cd8167 100644 --- a/pkg/commands/kvstoreentry/describe.go +++ b/pkg/commands/kvstoreentry/describe.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -18,17 +17,15 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data - Input fastly.GetKVStoreKeyInput + Input fastly.GetKVStoreKeyInput } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Get the value associated with a key").Alias("get") diff --git a/pkg/commands/kvstoreentry/kvstoreentry_test.go b/pkg/commands/kvstoreentry/kvstoreentry_test.go index fbcf406d9..9225d0aaf 100644 --- a/pkg/commands/kvstoreentry/kvstoreentry_test.go +++ b/pkg/commands/kvstoreentry/kvstoreentry_test.go @@ -14,6 +14,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/kvstoreentry" fstfmt "github.com/fastly/cli/pkg/fmt" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" "github.com/fastly/cli/pkg/threadsafe" @@ -128,11 +129,11 @@ func TestCreateCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) if testcase.Stdin != nil { - opts.Stdin = testcase.Stdin + opts.Input = testcase.Stdin } return opts, nil } @@ -251,8 +252,8 @@ SUCCESS: Deleted all keys from KV Store '%s' testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout threadsafe.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -309,8 +310,8 @@ func TestDescribeCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -367,8 +368,8 @@ func TestListCommand(t *testing.T) { testcase := testcase t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/kvstoreentry/list.go b/pkg/commands/kvstoreentry/list.go index 2cf3fd386..31f0f481e 100644 --- a/pkg/commands/kvstoreentry/list.go +++ b/pkg/commands/kvstoreentry/list.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -19,7 +18,6 @@ type ListCommand struct { cmd.JSONOutput consistency string - manifest manifest.Data Input fastly.ListKVStoreKeysInput } @@ -30,12 +28,11 @@ var ConsistencyOptions = []string{ } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List keys") diff --git a/pkg/commands/logging/azureblob/azureblob_integration_test.go b/pkg/commands/logging/azureblob/azureblob_integration_test.go index 0caf19261..54a56539a 100644 --- a/pkg/commands/logging/azureblob/azureblob_integration_test.go +++ b/pkg/commands/logging/azureblob/azureblob_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -54,8 +55,8 @@ func TestBlobStorageCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -127,8 +128,8 @@ func TestBlobStorageList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -172,8 +173,8 @@ func TestBlobStorageDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -219,8 +220,8 @@ func TestBlobStorageUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -266,8 +267,8 @@ func TestBlobStorageDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/azureblob/create.go b/pkg/commands/logging/azureblob/create.go index 41a9c8f6c..cee048b6a 100644 --- a/pkg/commands/logging/azureblob/create.go +++ b/pkg/commands/logging/azureblob/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an Azure Blob Storage logging endpoint. @@ -43,12 +44,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an Azure Blob Storage logging endpoint on a Fastly service version").Alias("add") @@ -83,7 +83,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -181,7 +181,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/azureblob/delete.go b/pkg/commands/logging/azureblob/delete.go index 8a92c228e..12b1cd75f 100644 --- a/pkg/commands/logging/azureblob/delete.go +++ b/pkg/commands/logging/azureblob/delete.go @@ -3,18 +3,17 @@ package azureblob import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an Azure Blob Storage logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteBlobStorageInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an Azure Blob Storage logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/azureblob/describe.go b/pkg/commands/logging/azureblob/describe.go index d37b6c60f..335f23e35 100644 --- a/pkg/commands/logging/azureblob/describe.go +++ b/pkg/commands/logging/azureblob/describe.go @@ -3,12 +3,12 @@ package azureblob import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an Azure Blob Storage logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetBlobStorageInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about an Azure Blob Storage logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/azureblob/list.go b/pkg/commands/logging/azureblob/list.go index 5f950a64d..1534fbbe1 100644 --- a/pkg/commands/logging/azureblob/list.go +++ b/pkg/commands/logging/azureblob/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Azure Blob Storage logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListBlobStoragesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Azure Blob Storage logging endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/azureblob/update.go b/pkg/commands/logging/azureblob/update.go index 5e0c6956f..51724ca36 100644 --- a/pkg/commands/logging/azureblob/update.go +++ b/pkg/commands/logging/azureblob/update.go @@ -3,13 +3,14 @@ package azureblob import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an Azure Blob Storage logging endpoint. @@ -43,12 +44,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update an Azure Blob Storage logging endpoint on a Fastly service version") @@ -86,7 +86,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -180,7 +180,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/bigquery/bigquery_integration_test.go b/pkg/commands/logging/bigquery/bigquery_integration_test.go index 324ea7c96..a11cec0ac 100644 --- a/pkg/commands/logging/bigquery/bigquery_integration_test.go +++ b/pkg/commands/logging/bigquery/bigquery_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestBigQueryCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestBigQueryList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestBigQueryDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestBigQueryUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestBigQueryDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/bigquery/create.go b/pkg/commands/logging/bigquery/create.go index d97d082b8..c2c822793 100644 --- a/pkg/commands/logging/bigquery/create.go +++ b/pkg/commands/logging/bigquery/create.go @@ -39,12 +39,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a BigQuery logging endpoint on a Fastly service version").Alias("add") @@ -73,7 +72,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -140,7 +139,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/bigquery/delete.go b/pkg/commands/logging/bigquery/delete.go index f18a1370c..a9c20ddf2 100644 --- a/pkg/commands/logging/bigquery/delete.go +++ b/pkg/commands/logging/bigquery/delete.go @@ -3,18 +3,17 @@ package bigquery import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a BigQuery logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteBigQueryInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a BigQuery logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/bigquery/describe.go b/pkg/commands/logging/bigquery/describe.go index bdab599cd..9a08ed428 100644 --- a/pkg/commands/logging/bigquery/describe.go +++ b/pkg/commands/logging/bigquery/describe.go @@ -3,12 +3,12 @@ package bigquery import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a BigQuery logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetBigQueryInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a BigQuery logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/bigquery/list.go b/pkg/commands/logging/bigquery/list.go index 7b8b10b70..4966edd49 100644 --- a/pkg/commands/logging/bigquery/list.go +++ b/pkg/commands/logging/bigquery/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list BigQuery logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListBigQueriesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List BigQuery endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/bigquery/update.go b/pkg/commands/logging/bigquery/update.go index 570069bb3..868eb6bcd 100644 --- a/pkg/commands/logging/bigquery/update.go +++ b/pkg/commands/logging/bigquery/update.go @@ -40,12 +40,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a BigQuery logging endpoint on a Fastly service version") @@ -75,7 +74,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -143,7 +142,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go index c4a3636f6..de88f74e7 100644 --- a/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go +++ b/pkg/commands/logging/cloudfiles/cloudfiles_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -53,8 +54,8 @@ func TestCloudfilesCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -126,8 +127,8 @@ func TestCloudfilesList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestCloudfilesDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -218,8 +219,8 @@ func TestCloudfilesUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -265,8 +266,8 @@ func TestCloudfilesDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/cloudfiles/create.go b/pkg/commands/logging/cloudfiles/create.go index 56912c910..75e11edc7 100644 --- a/pkg/commands/logging/cloudfiles/create.go +++ b/pkg/commands/logging/cloudfiles/create.go @@ -45,12 +45,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Cloudfiles logging endpoint on a Fastly service version").Alias("add") @@ -84,7 +83,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -179,7 +178,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/cloudfiles/delete.go b/pkg/commands/logging/cloudfiles/delete.go index 417f23a75..f0c87bfde 100644 --- a/pkg/commands/logging/cloudfiles/delete.go +++ b/pkg/commands/logging/cloudfiles/delete.go @@ -3,18 +3,17 @@ package cloudfiles import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Cloudfiles logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteCloudfilesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Cloudfiles logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/cloudfiles/describe.go b/pkg/commands/logging/cloudfiles/describe.go index 8cb67b9a5..7d0b8ff13 100644 --- a/pkg/commands/logging/cloudfiles/describe.go +++ b/pkg/commands/logging/cloudfiles/describe.go @@ -3,12 +3,12 @@ package cloudfiles import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Cloudfiles logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetCloudfilesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Cloudfiles logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/cloudfiles/list.go b/pkg/commands/logging/cloudfiles/list.go index 2cca155f7..f9b9e793d 100644 --- a/pkg/commands/logging/cloudfiles/list.go +++ b/pkg/commands/logging/cloudfiles/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Cloudfiles logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListCloudfilesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Cloudfiles endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/cloudfiles/update.go b/pkg/commands/logging/cloudfiles/update.go index e547ccde5..30aeff046 100644 --- a/pkg/commands/logging/cloudfiles/update.go +++ b/pkg/commands/logging/cloudfiles/update.go @@ -44,12 +44,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Cloudfiles logging endpoint on a Fastly service version") @@ -85,7 +84,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -179,7 +178,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/datadog/create.go b/pkg/commands/logging/datadog/create.go index 781b64240..2e5c8e7ff 100644 --- a/pkg/commands/logging/datadog/create.go +++ b/pkg/commands/logging/datadog/create.go @@ -3,13 +3,14 @@ package datadog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Datadog logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Datadog logging endpoint on a Fastly service version").Alias("add") @@ -65,7 +65,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -118,7 +118,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/datadog/datadog_integration_test.go b/pkg/commands/logging/datadog/datadog_integration_test.go index bce6272b1..0edbdd848 100644 --- a/pkg/commands/logging/datadog/datadog_integration_test.go +++ b/pkg/commands/logging/datadog/datadog_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestDatadogCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestDatadogList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestDatadogDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestDatadogUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestDatadogDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/datadog/delete.go b/pkg/commands/logging/datadog/delete.go index 29435ba77..d53570cbe 100644 --- a/pkg/commands/logging/datadog/delete.go +++ b/pkg/commands/logging/datadog/delete.go @@ -3,18 +3,17 @@ package datadog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Datadog logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteDatadogInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Datadog logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/datadog/describe.go b/pkg/commands/logging/datadog/describe.go index bf0700611..716fdbcef 100644 --- a/pkg/commands/logging/datadog/describe.go +++ b/pkg/commands/logging/datadog/describe.go @@ -3,12 +3,12 @@ package datadog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Datadog logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetDatadogInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Datadog logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/datadog/list.go b/pkg/commands/logging/datadog/list.go index 789a6045c..c32a06142 100644 --- a/pkg/commands/logging/datadog/list.go +++ b/pkg/commands/logging/datadog/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Datadog logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListDatadogInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Datadog endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/datadog/update.go b/pkg/commands/logging/datadog/update.go index 445d4ad70..631b3de2e 100644 --- a/pkg/commands/logging/datadog/update.go +++ b/pkg/commands/logging/datadog/update.go @@ -3,13 +3,14 @@ package datadog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Datadog logging endpoint. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Datadog logging endpoint on a Fastly service version") @@ -67,7 +67,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -123,7 +123,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/digitalocean/create.go b/pkg/commands/logging/digitalocean/create.go index 2d007a388..94ed0b83f 100644 --- a/pkg/commands/logging/digitalocean/create.go +++ b/pkg/commands/logging/digitalocean/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a DigitalOcean Spaces logging endpoint. @@ -43,12 +44,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a DigitalOcean Spaces logging endpoint on a Fastly service version").Alias("add") @@ -83,7 +83,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -177,7 +177,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/digitalocean/delete.go b/pkg/commands/logging/digitalocean/delete.go index 3b2f628eb..67ede2c56 100644 --- a/pkg/commands/logging/digitalocean/delete.go +++ b/pkg/commands/logging/digitalocean/delete.go @@ -3,18 +3,17 @@ package digitalocean import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a DigitalOcean Spaces logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteDigitalOceanInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a DigitalOcean Spaces logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/digitalocean/describe.go b/pkg/commands/logging/digitalocean/describe.go index 6037e9f81..b2e5d201a 100644 --- a/pkg/commands/logging/digitalocean/describe.go +++ b/pkg/commands/logging/digitalocean/describe.go @@ -3,12 +3,12 @@ package digitalocean import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a DigitalOcean Spaces logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetDigitalOceanInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a DigitalOcean Spaces logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go index 14788c45a..8cc3220ba 100644 --- a/pkg/commands/logging/digitalocean/digitalocean_integration_test.go +++ b/pkg/commands/logging/digitalocean/digitalocean_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -53,8 +54,8 @@ func TestDigitalOceanCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -126,8 +127,8 @@ func TestDigitalOceanList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestDigitalOceanDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -218,8 +219,8 @@ func TestDigitalOceanUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -265,8 +266,8 @@ func TestDigitalOceanDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/digitalocean/list.go b/pkg/commands/logging/digitalocean/list.go index c7808374d..eb8e8df06 100644 --- a/pkg/commands/logging/digitalocean/list.go +++ b/pkg/commands/logging/digitalocean/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list DigitalOcean Spaces logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListDigitalOceansInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List DigitalOcean Spaces logging endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/digitalocean/update.go b/pkg/commands/logging/digitalocean/update.go index e671b4688..f55c4d18d 100644 --- a/pkg/commands/logging/digitalocean/update.go +++ b/pkg/commands/logging/digitalocean/update.go @@ -3,13 +3,14 @@ package digitalocean import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a DigitalOcean Spaces logging endpoint. @@ -43,12 +44,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a DigitalOcean Spaces logging endpoint on a Fastly service version") @@ -84,7 +84,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -178,7 +178,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/elasticsearch/create.go b/pkg/commands/logging/elasticsearch/create.go index 36dbeb140..0b570b0b3 100644 --- a/pkg/commands/logging/elasticsearch/create.go +++ b/pkg/commands/logging/elasticsearch/create.go @@ -3,13 +3,14 @@ package elasticsearch import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an Elasticsearch logging endpoint. @@ -42,12 +43,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an Elasticsearch logging endpoint on a Fastly service version").Alias("add") @@ -76,7 +76,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -169,7 +169,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/elasticsearch/delete.go b/pkg/commands/logging/elasticsearch/delete.go index bfdba2960..5dd751e25 100644 --- a/pkg/commands/logging/elasticsearch/delete.go +++ b/pkg/commands/logging/elasticsearch/delete.go @@ -3,18 +3,17 @@ package elasticsearch import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an Elasticsearch logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteElasticsearchInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an Elasticsearch logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/elasticsearch/describe.go b/pkg/commands/logging/elasticsearch/describe.go index d90ee52e3..cc72397f1 100644 --- a/pkg/commands/logging/elasticsearch/describe.go +++ b/pkg/commands/logging/elasticsearch/describe.go @@ -3,12 +3,12 @@ package elasticsearch import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an Elasticsearch logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetElasticsearchInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about an Elasticsearch logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go index 097f76681..690898fdd 100644 --- a/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go +++ b/pkg/commands/logging/elasticsearch/elasticsearch_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestElasticsearchCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestElasticsearchList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestElasticsearchDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestElasticsearchUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestElasticsearchDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/elasticsearch/list.go b/pkg/commands/logging/elasticsearch/list.go index a2caaec94..20af9da99 100644 --- a/pkg/commands/logging/elasticsearch/list.go +++ b/pkg/commands/logging/elasticsearch/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Elasticsearch logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListElasticsearchInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Elasticsearch endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/elasticsearch/update.go b/pkg/commands/logging/elasticsearch/update.go index bf804f303..360311590 100644 --- a/pkg/commands/logging/elasticsearch/update.go +++ b/pkg/commands/logging/elasticsearch/update.go @@ -3,13 +3,14 @@ package elasticsearch import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an Elasticsearch logging endpoint. @@ -43,12 +44,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update an Elasticsearch logging endpoint on a Fastly service version") @@ -78,7 +78,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -175,7 +175,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/ftp/create.go b/pkg/commands/logging/ftp/create.go index c9409d87d..927e2ee86 100644 --- a/pkg/commands/logging/ftp/create.go +++ b/pkg/commands/logging/ftp/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an FTP logging endpoint. @@ -41,12 +42,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an FTP logging endpoint on a Fastly service version").Alias("add") @@ -79,7 +79,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -165,7 +165,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/ftp/delete.go b/pkg/commands/logging/ftp/delete.go index b623ade23..871eb1b2d 100644 --- a/pkg/commands/logging/ftp/delete.go +++ b/pkg/commands/logging/ftp/delete.go @@ -3,18 +3,17 @@ package ftp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an FTP logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteFTPInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an FTP logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/ftp/describe.go b/pkg/commands/logging/ftp/describe.go index 7d7cd9c7f..5ddcedc0c 100644 --- a/pkg/commands/logging/ftp/describe.go +++ b/pkg/commands/logging/ftp/describe.go @@ -3,12 +3,12 @@ package ftp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an FTP logging endpoint. @@ -16,26 +16,24 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetFTPInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about an FTP logging endpoint on a Fastly service version").Alias("get") c.RegisterFlagBool(c.JSONFlag()) // --json c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -63,7 +61,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/ftp/ftp_integration_test.go b/pkg/commands/logging/ftp/ftp_integration_test.go index 9c723dce4..fb3c3cde1 100644 --- a/pkg/commands/logging/ftp/ftp_integration_test.go +++ b/pkg/commands/logging/ftp/ftp_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -53,8 +54,8 @@ func TestFTPCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -126,8 +127,8 @@ func TestFTPList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestFTPDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -218,8 +219,8 @@ func TestFTPUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -265,8 +266,8 @@ func TestFTPDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/ftp/list.go b/pkg/commands/logging/ftp/list.go index 4e0d4aa21..fe52ed62e 100644 --- a/pkg/commands/logging/ftp/list.go +++ b/pkg/commands/logging/ftp/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list FTP logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListFTPsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List FTP endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/ftp/update.go b/pkg/commands/logging/ftp/update.go index 692095bfd..a3b4e77d7 100644 --- a/pkg/commands/logging/ftp/update.go +++ b/pkg/commands/logging/ftp/update.go @@ -3,13 +3,14 @@ package ftp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an FTP logging endpoint. @@ -42,12 +43,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update an FTP logging endpoint on a Fastly service version") @@ -81,7 +81,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -172,7 +172,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/gcs/create.go b/pkg/commands/logging/gcs/create.go index 8a2c974da..51a5f31fb 100644 --- a/pkg/commands/logging/gcs/create.go +++ b/pkg/commands/logging/gcs/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a GCS logging endpoint. @@ -42,12 +43,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a GCS logging endpoint on a Fastly service version").Alias("add") @@ -80,7 +80,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -161,7 +161,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/gcs/delete.go b/pkg/commands/logging/gcs/delete.go index 6120652e6..b545ebbc1 100644 --- a/pkg/commands/logging/gcs/delete.go +++ b/pkg/commands/logging/gcs/delete.go @@ -3,18 +3,17 @@ package gcs import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a GCS logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteGCSInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a GCS logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/gcs/describe.go b/pkg/commands/logging/gcs/describe.go index d7d03c7ed..97a938fab 100644 --- a/pkg/commands/logging/gcs/describe.go +++ b/pkg/commands/logging/gcs/describe.go @@ -3,12 +3,12 @@ package gcs import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a GCS logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetGCSInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a GCS logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/gcs/gcs_integration_test.go b/pkg/commands/logging/gcs/gcs_integration_test.go index 3059abbf4..ccac331a4 100644 --- a/pkg/commands/logging/gcs/gcs_integration_test.go +++ b/pkg/commands/logging/gcs/gcs_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -53,8 +54,8 @@ func TestGCSCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -126,8 +127,8 @@ func TestGCSList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestGCSDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -218,8 +219,8 @@ func TestGCSUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -265,8 +266,8 @@ func TestGCSDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/gcs/list.go b/pkg/commands/logging/gcs/list.go index 12db8738d..e1b1fabc7 100644 --- a/pkg/commands/logging/gcs/list.go +++ b/pkg/commands/logging/gcs/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list GCS logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListGCSsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List GCS endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/gcs/update.go b/pkg/commands/logging/gcs/update.go index 0fd229a86..c62a04336 100644 --- a/pkg/commands/logging/gcs/update.go +++ b/pkg/commands/logging/gcs/update.go @@ -3,13 +3,14 @@ package gcs import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a GCS logging endpoint. @@ -42,12 +43,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a GCS logging endpoint on a Fastly service version") @@ -81,7 +81,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -157,7 +157,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/googlepubsub/create.go b/pkg/commands/logging/googlepubsub/create.go index 08b1bc2dc..6f90b4322 100644 --- a/pkg/commands/logging/googlepubsub/create.go +++ b/pkg/commands/logging/googlepubsub/create.go @@ -3,13 +3,14 @@ package googlepubsub import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Google Cloud Pub/Sub logging endpoint. @@ -36,12 +37,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Google Cloud Pub/Sub logging endpoint on a Fastly service version").Alias("add") @@ -69,7 +69,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -129,7 +129,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/googlepubsub/delete.go b/pkg/commands/logging/googlepubsub/delete.go index a684adf57..d2342240d 100644 --- a/pkg/commands/logging/googlepubsub/delete.go +++ b/pkg/commands/logging/googlepubsub/delete.go @@ -3,18 +3,17 @@ package googlepubsub import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Google Cloud Pub/Sub logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeletePubsubInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Google Cloud Pub/Sub logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/googlepubsub/describe.go b/pkg/commands/logging/googlepubsub/describe.go index a6d93e3bc..011e2267f 100644 --- a/pkg/commands/logging/googlepubsub/describe.go +++ b/pkg/commands/logging/googlepubsub/describe.go @@ -3,12 +3,12 @@ package googlepubsub import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Google Cloud Pub/Sub logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetPubsubInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Google Cloud Pub/Sub logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go index f830da83b..b151699e9 100644 --- a/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go +++ b/pkg/commands/logging/googlepubsub/googlepubsub_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestGooglePubSubCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestGooglePubSubList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestGooglePubSubDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestGooglePubSubUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestGooglePubSubDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/googlepubsub/list.go b/pkg/commands/logging/googlepubsub/list.go index ca132487d..a9ae5b661 100644 --- a/pkg/commands/logging/googlepubsub/list.go +++ b/pkg/commands/logging/googlepubsub/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Google Cloud Pub/Sub logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListPubsubsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Google Cloud Pub/Sub endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/googlepubsub/update.go b/pkg/commands/logging/googlepubsub/update.go index a9c1a5051..106ed3f7f 100644 --- a/pkg/commands/logging/googlepubsub/update.go +++ b/pkg/commands/logging/googlepubsub/update.go @@ -3,13 +3,14 @@ package googlepubsub import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Google Cloud Pub/Sub logging endpoint. @@ -37,12 +38,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Google Cloud Pub/Sub logging endpoint on a Fastly service version") @@ -71,7 +71,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -132,7 +132,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/heroku/create.go b/pkg/commands/logging/heroku/create.go index 92e485e2c..c23505289 100644 --- a/pkg/commands/logging/heroku/create.go +++ b/pkg/commands/logging/heroku/create.go @@ -3,13 +3,14 @@ package heroku import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Heroku logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Heroku logging endpoint on a Fastly service version").Alias("add") @@ -64,7 +64,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -117,7 +117,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/heroku/delete.go b/pkg/commands/logging/heroku/delete.go index 5c50640d2..0572798f8 100644 --- a/pkg/commands/logging/heroku/delete.go +++ b/pkg/commands/logging/heroku/delete.go @@ -3,18 +3,17 @@ package heroku import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Heroku logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteHerokuInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Heroku logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/heroku/describe.go b/pkg/commands/logging/heroku/describe.go index 938d72599..8c49da15b 100644 --- a/pkg/commands/logging/heroku/describe.go +++ b/pkg/commands/logging/heroku/describe.go @@ -3,12 +3,12 @@ package heroku import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Heroku logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetHerokuInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Heroku logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/heroku/heroku_integration_test.go b/pkg/commands/logging/heroku/heroku_integration_test.go index 9d2f2646e..0de173704 100644 --- a/pkg/commands/logging/heroku/heroku_integration_test.go +++ b/pkg/commands/logging/heroku/heroku_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestHerokuCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestHerokuList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestHerokuDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestHerokuUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestHerokuDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/heroku/list.go b/pkg/commands/logging/heroku/list.go index dd8825882..e4345164f 100644 --- a/pkg/commands/logging/heroku/list.go +++ b/pkg/commands/logging/heroku/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Heroku logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListHerokusInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Heroku endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/heroku/update.go b/pkg/commands/logging/heroku/update.go index dbe09a5d6..d46c0b406 100644 --- a/pkg/commands/logging/heroku/update.go +++ b/pkg/commands/logging/heroku/update.go @@ -3,13 +3,14 @@ package heroku import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Heroku logging endpoint. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Heroku logging endpoint on a Fastly service version") @@ -66,7 +66,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -123,7 +123,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/honeycomb/create.go b/pkg/commands/logging/honeycomb/create.go index a018b62e8..93ff7431b 100644 --- a/pkg/commands/logging/honeycomb/create.go +++ b/pkg/commands/logging/honeycomb/create.go @@ -3,13 +3,14 @@ package honeycomb import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Honeycomb logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Honeycomb logging endpoint on a Fastly service version").Alias("add") @@ -65,7 +65,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -117,7 +117,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/honeycomb/delete.go b/pkg/commands/logging/honeycomb/delete.go index 9f93b5279..9f6fe7672 100644 --- a/pkg/commands/logging/honeycomb/delete.go +++ b/pkg/commands/logging/honeycomb/delete.go @@ -3,18 +3,17 @@ package honeycomb import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Honeycomb logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteHoneycombInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Honeycomb logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/honeycomb/describe.go b/pkg/commands/logging/honeycomb/describe.go index 554fe1779..9a6b79e63 100644 --- a/pkg/commands/logging/honeycomb/describe.go +++ b/pkg/commands/logging/honeycomb/describe.go @@ -3,12 +3,12 @@ package honeycomb import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Honeycomb logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetHoneycombInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Honeycomb logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go index 5e83d4202..d7ac97ff3 100644 --- a/pkg/commands/logging/honeycomb/honeycomb_integration_test.go +++ b/pkg/commands/logging/honeycomb/honeycomb_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestHoneycombCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestHoneycombList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestHoneycombDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestHoneycombUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestHoneycombDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/honeycomb/list.go b/pkg/commands/logging/honeycomb/list.go index 8af9bd835..508594f94 100644 --- a/pkg/commands/logging/honeycomb/list.go +++ b/pkg/commands/logging/honeycomb/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Honeycomb logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListHoneycombsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Honeycomb endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/honeycomb/update.go b/pkg/commands/logging/honeycomb/update.go index 3fe7905bd..7dd9550d8 100644 --- a/pkg/commands/logging/honeycomb/update.go +++ b/pkg/commands/logging/honeycomb/update.go @@ -3,13 +3,14 @@ package honeycomb import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Honeycomb logging endpoint. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Honeycomb logging endpoint on a Fastly service version") @@ -67,7 +67,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -123,7 +123,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/https/create.go b/pkg/commands/logging/https/create.go index fbb5c2a1f..5afb0a62d 100644 --- a/pkg/commands/logging/https/create.go +++ b/pkg/commands/logging/https/create.go @@ -3,13 +3,14 @@ package https import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an HTTPS logging endpoint. @@ -44,12 +45,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an HTTPS logging endpoint on a Fastly service version").Alias("add") @@ -82,7 +82,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -184,7 +184,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/https/delete.go b/pkg/commands/logging/https/delete.go index 8e9fcb484..052d0d7b4 100644 --- a/pkg/commands/logging/https/delete.go +++ b/pkg/commands/logging/https/delete.go @@ -3,18 +3,17 @@ package https import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an HTTPS logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteHTTPSInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an HTTPS logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/https/describe.go b/pkg/commands/logging/https/describe.go index 7b05200d5..b378fd7d6 100644 --- a/pkg/commands/logging/https/describe.go +++ b/pkg/commands/logging/https/describe.go @@ -3,12 +3,12 @@ package https import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an HTTPS logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetHTTPSInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about an HTTPS logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/https/https_integration_test.go b/pkg/commands/logging/https/https_integration_test.go index 04375291c..960d58ede 100644 --- a/pkg/commands/logging/https/https_integration_test.go +++ b/pkg/commands/logging/https/https_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestHTTPSCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestHTTPSList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestHTTPSDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestHTTPSUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestHTTPSDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/https/list.go b/pkg/commands/logging/https/list.go index 8be2ca602..5af3ec9d7 100644 --- a/pkg/commands/logging/https/list.go +++ b/pkg/commands/logging/https/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list HTTPS logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListHTTPSInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List HTTPS endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/https/update.go b/pkg/commands/logging/https/update.go index bb9edbc80..1cbb5aab7 100644 --- a/pkg/commands/logging/https/update.go +++ b/pkg/commands/logging/https/update.go @@ -3,13 +3,14 @@ package https import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an HTTPS logging endpoint. @@ -45,12 +46,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update an HTTPS logging endpoint on a Fastly service version") @@ -84,7 +84,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -189,7 +189,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/kafka/create.go b/pkg/commands/logging/kafka/create.go index 6fcf2b856..f9d3a387b 100644 --- a/pkg/commands/logging/kafka/create.go +++ b/pkg/commands/logging/kafka/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Kafka logging endpoint. @@ -47,12 +48,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Kafka logging endpoint on a Fastly service version").Alias("add") @@ -84,7 +84,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -200,7 +200,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/kafka/delete.go b/pkg/commands/logging/kafka/delete.go index bdf8f0a43..a5a4d6050 100644 --- a/pkg/commands/logging/kafka/delete.go +++ b/pkg/commands/logging/kafka/delete.go @@ -3,18 +3,17 @@ package kafka import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Kafka logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteKafkaInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Kafka logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/kafka/describe.go b/pkg/commands/logging/kafka/describe.go index d61c23094..511680e05 100644 --- a/pkg/commands/logging/kafka/describe.go +++ b/pkg/commands/logging/kafka/describe.go @@ -3,12 +3,12 @@ package kafka import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Kafka logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetKafkaInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Kafka logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/kafka/kafka_integration_test.go b/pkg/commands/logging/kafka/kafka_integration_test.go index 8a01f7b18..2c2e4793e 100644 --- a/pkg/commands/logging/kafka/kafka_integration_test.go +++ b/pkg/commands/logging/kafka/kafka_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestKafkaCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestKafkaList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestKafkaDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -219,8 +220,8 @@ func TestKafkaUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -266,8 +267,8 @@ func TestKafkaDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -331,9 +332,9 @@ func listKafkasOK(i *fastly.ListKafkasInput) ([]*fastly.Kafka, error) { FormatVersion: 2, ParseLogKeyvals: false, RequestMaxBytes: 0, - AuthMethod: "", - User: "", - Password: "", + AuthMethod: "plain", + User: "user", + Password: "password", }, { ServiceID: i.ServiceID, @@ -354,9 +355,9 @@ func listKafkasOK(i *fastly.ListKafkasInput) ([]*fastly.Kafka, error) { FormatVersion: 2, ParseLogKeyvals: false, RequestMaxBytes: 0, - AuthMethod: "", - User: "", - Password: "", + AuthMethod: "plain", + User: "user", + Password: "password", }, }, nil } @@ -397,9 +398,9 @@ Version: 1 Placement: none Parse log key-values: false Max batch size: 0 - SASL authentication method: - SASL authentication username: - SASL authentication password: + SASL authentication method: plain + SASL authentication username: user + SASL authentication password: password Kafka 2/2 Service ID: 123 Version: 1 @@ -419,10 +420,10 @@ Version: 1 Placement: none Parse log key-values: false Max batch size: 0 - SASL authentication method: - SASL authentication username: - SASL authentication password: -`) + " \n\n" + SASL authentication method: plain + SASL authentication username: user + SASL authentication password: password + `) + "\n\n" func getKafkaOK(i *fastly.GetKafkaInput) (*fastly.Kafka, error) { return &fastly.Kafka{ diff --git a/pkg/commands/logging/kafka/list.go b/pkg/commands/logging/kafka/list.go index 9b21cdffc..3c9b6c0b5 100644 --- a/pkg/commands/logging/kafka/list.go +++ b/pkg/commands/logging/kafka/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Kafka logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListKafkasInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Kafka endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/kafka/update.go b/pkg/commands/logging/kafka/update.go index 522eed61f..e703bd8e6 100644 --- a/pkg/commands/logging/kafka/update.go +++ b/pkg/commands/logging/kafka/update.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Kafka logging endpoint. @@ -49,12 +50,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Kafka logging endpoint on a Fastly service version") @@ -87,7 +87,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -213,7 +213,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/kinesis/create.go b/pkg/commands/logging/kinesis/create.go index 7e754046d..1b006ad6c 100644 --- a/pkg/commands/logging/kinesis/create.go +++ b/pkg/commands/logging/kinesis/create.go @@ -41,12 +41,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an Amazon Kinesis logging endpoint on a Fastly service version").Alias("add") @@ -78,7 +77,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -161,7 +160,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/kinesis/delete.go b/pkg/commands/logging/kinesis/delete.go index b61cc8653..1a1f44737 100644 --- a/pkg/commands/logging/kinesis/delete.go +++ b/pkg/commands/logging/kinesis/delete.go @@ -3,18 +3,17 @@ package kinesis import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an Amazon Kinesis logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteKinesisInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Kinesis logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -66,7 +64,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/kinesis/describe.go b/pkg/commands/logging/kinesis/describe.go index 4bdf3372a..fb2390410 100644 --- a/pkg/commands/logging/kinesis/describe.go +++ b/pkg/commands/logging/kinesis/describe.go @@ -3,12 +3,12 @@ package kinesis import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an Amazon Kinesis logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetKinesisInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Kinesis logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/kinesis/kinesis_integration_test.go b/pkg/commands/logging/kinesis/kinesis_integration_test.go index f44415567..b01bcfb83 100644 --- a/pkg/commands/logging/kinesis/kinesis_integration_test.go +++ b/pkg/commands/logging/kinesis/kinesis_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -87,8 +88,8 @@ func TestKinesisCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -160,8 +161,8 @@ func TestKinesisList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -205,8 +206,8 @@ func TestKinesisDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -252,8 +253,8 @@ func TestKinesisUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -299,8 +300,8 @@ func TestKinesisDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/kinesis/list.go b/pkg/commands/logging/kinesis/list.go index a9fdf5412..b5d71a9ff 100644 --- a/pkg/commands/logging/kinesis/list.go +++ b/pkg/commands/logging/kinesis/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Amazon Kinesis logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListKinesisInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Kinesis endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/kinesis/update.go b/pkg/commands/logging/kinesis/update.go index 10515ada3..563f8bbb1 100644 --- a/pkg/commands/logging/kinesis/update.go +++ b/pkg/commands/logging/kinesis/update.go @@ -3,13 +3,14 @@ package kinesis import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an Amazon Kinesis logging endpoint. @@ -37,12 +38,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Kinesis logging endpoint on a Fastly service version") @@ -72,7 +72,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -141,7 +141,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/loggly/create.go b/pkg/commands/logging/loggly/create.go index d3808c3e8..fe457d98b 100644 --- a/pkg/commands/logging/loggly/create.go +++ b/pkg/commands/logging/loggly/create.go @@ -3,13 +3,14 @@ package loggly import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Loggly logging endpoint. @@ -32,12 +33,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Loggly logging endpoint on a Fastly service version").Alias("add") @@ -63,7 +63,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -112,7 +112,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/loggly/delete.go b/pkg/commands/logging/loggly/delete.go index 6cf69c81b..956078a5f 100644 --- a/pkg/commands/logging/loggly/delete.go +++ b/pkg/commands/logging/loggly/delete.go @@ -3,18 +3,17 @@ package loggly import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Loggly logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteLogglyInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Loggly logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/loggly/describe.go b/pkg/commands/logging/loggly/describe.go index dd1aaad4f..8567fc7a5 100644 --- a/pkg/commands/logging/loggly/describe.go +++ b/pkg/commands/logging/loggly/describe.go @@ -3,12 +3,12 @@ package loggly import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Loggly logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetLogglyInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Loggly logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/loggly/list.go b/pkg/commands/logging/loggly/list.go index a26456c63..382af5e34 100644 --- a/pkg/commands/logging/loggly/list.go +++ b/pkg/commands/logging/loggly/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Loggly logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListLogglyInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Loggly endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/loggly/loggly_integration_test.go b/pkg/commands/logging/loggly/loggly_integration_test.go index 6f63e3843..403f61b8f 100644 --- a/pkg/commands/logging/loggly/loggly_integration_test.go +++ b/pkg/commands/logging/loggly/loggly_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestLogglyCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestLogglyList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestLogglyDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestLogglyUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestLogglyDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/loggly/update.go b/pkg/commands/logging/loggly/update.go index c6d2f9e3c..3ca8fa8c6 100644 --- a/pkg/commands/logging/loggly/update.go +++ b/pkg/commands/logging/loggly/update.go @@ -3,13 +3,14 @@ package loggly import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Loggly logging endpoint. @@ -33,12 +34,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Loggly logging endpoint on a Fastly service version") @@ -65,7 +65,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -117,7 +117,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/logshuttle/create.go b/pkg/commands/logging/logshuttle/create.go index e37fdd8ef..c825f472b 100644 --- a/pkg/commands/logging/logshuttle/create.go +++ b/pkg/commands/logging/logshuttle/create.go @@ -3,13 +3,14 @@ package logshuttle import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Logshuttle logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Logshuttle logging endpoint on a Fastly service version").Alias("add") @@ -64,7 +64,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -117,7 +117,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/logshuttle/delete.go b/pkg/commands/logging/logshuttle/delete.go index 824b46807..ab434a3d7 100644 --- a/pkg/commands/logging/logshuttle/delete.go +++ b/pkg/commands/logging/logshuttle/delete.go @@ -3,18 +3,17 @@ package logshuttle import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Logshuttle logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteLogshuttleInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Logshuttle logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/logshuttle/describe.go b/pkg/commands/logging/logshuttle/describe.go index d24ee1527..aeaf3669e 100644 --- a/pkg/commands/logging/logshuttle/describe.go +++ b/pkg/commands/logging/logshuttle/describe.go @@ -3,12 +3,12 @@ package logshuttle import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Logshuttle logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetLogshuttleInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Logshuttle logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/logshuttle/list.go b/pkg/commands/logging/logshuttle/list.go index f834ddb9f..bb9db397d 100644 --- a/pkg/commands/logging/logshuttle/list.go +++ b/pkg/commands/logging/logshuttle/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Logshuttle logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListLogshuttlesInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Logshuttle endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go index 2b2bc1cf0..77984e463 100644 --- a/pkg/commands/logging/logshuttle/logshuttle_integration_test.go +++ b/pkg/commands/logging/logshuttle/logshuttle_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestLogshuttleCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestLogshuttleList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestLogshuttleDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestLogshuttleUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestLogshuttleDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/logshuttle/update.go b/pkg/commands/logging/logshuttle/update.go index 3641661f8..5578e5c50 100644 --- a/pkg/commands/logging/logshuttle/update.go +++ b/pkg/commands/logging/logshuttle/update.go @@ -3,13 +3,14 @@ package logshuttle import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Logshuttle logging endpoint. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Logshuttle logging endpoint on a Fastly service version") @@ -66,7 +66,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -124,7 +124,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/newrelic/create.go b/pkg/commands/logging/newrelic/create.go index 823e1324f..c6482ae12 100644 --- a/pkg/commands/logging/newrelic/create.go +++ b/pkg/commands/logging/newrelic/create.go @@ -3,19 +3,18 @@ package newrelic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an appropriate resource. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceName cmd.OptionalServiceNameID @@ -33,12 +32,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create an New Relic logging endpoint attached to the specified service version").Alias("add") @@ -65,7 +63,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -83,7 +81,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelic/delete.go b/pkg/commands/logging/newrelic/delete.go index f83e5b195..ad4c4115a 100644 --- a/pkg/commands/logging/newrelic/delete.go +++ b/pkg/commands/logging/newrelic/delete.go @@ -3,21 +3,20 @@ package newrelic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete the New Relic Logs logging object for a particular service and version").Alias("remove") @@ -38,7 +37,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -56,7 +55,6 @@ type DeleteCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -67,7 +65,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelic/describe.go b/pkg/commands/logging/newrelic/describe.go index 5dd422a8c..5d5bcc0d8 100644 --- a/pkg/commands/logging/newrelic/describe.go +++ b/pkg/commands/logging/newrelic/describe.go @@ -3,21 +3,20 @@ package newrelic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Get the details of a New Relic Logs logging object for a particular service and version").Alias("get") @@ -35,7 +34,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -68,7 +66,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelic/list.go b/pkg/commands/logging/newrelic/list.go index 66ddf999a..d66fe4077 100644 --- a/pkg/commands/logging/newrelic/list.go +++ b/pkg/commands/logging/newrelic/list.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List all of the New Relic Logs logging objects for a particular service and version") @@ -35,7 +34,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelic/newrelic_test.go b/pkg/commands/logging/newrelic/newrelic_test.go index 6799e9ce0..c5e38deb9 100644 --- a/pkg/commands/logging/newrelic/newrelic_test.go +++ b/pkg/commands/logging/newrelic/newrelic_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -76,8 +77,8 @@ func TestNewRelicCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -154,8 +155,8 @@ func TestNewRelicDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -219,8 +220,8 @@ func TestNewRelicDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -288,8 +289,8 @@ func TestNewRelicList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -374,8 +375,8 @@ func TestNewRelicUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/logging/newrelic/update.go b/pkg/commands/logging/newrelic/update.go index ce48c0d28..6abe4e2cc 100644 --- a/pkg/commands/logging/newrelic/update.go +++ b/pkg/commands/logging/newrelic/update.go @@ -4,13 +4,13 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an appropriate resource. @@ -25,7 +25,6 @@ type UpdateCommand struct { format cmd.OptionalString formatVersion cmd.OptionalInt key cmd.OptionalString - manifest manifest.Data newName cmd.OptionalString placement cmd.OptionalString region cmd.OptionalString @@ -33,12 +32,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a New Relic Logs logging object for a particular service and version") @@ -66,7 +64,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -84,7 +82,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelicotlp/create.go b/pkg/commands/logging/newrelicotlp/create.go index cc19c9a8e..76f7bfaef 100644 --- a/pkg/commands/logging/newrelicotlp/create.go +++ b/pkg/commands/logging/newrelicotlp/create.go @@ -3,19 +3,18 @@ package newrelicotlp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an appropriate resource. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceName cmd.OptionalServiceNameID @@ -34,12 +33,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create an New Relic logging endpoint attached to the specified service version").Alias("add") @@ -67,7 +65,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -85,7 +83,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelicotlp/delete.go b/pkg/commands/logging/newrelicotlp/delete.go index c591be1fb..1867b87f3 100644 --- a/pkg/commands/logging/newrelicotlp/delete.go +++ b/pkg/commands/logging/newrelicotlp/delete.go @@ -3,21 +3,20 @@ package newrelicotlp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete the New Relic OTLP Logs logging object for a particular service and version").Alias("remove") @@ -38,7 +37,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -56,7 +55,6 @@ type DeleteCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -67,7 +65,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelicotlp/describe.go b/pkg/commands/logging/newrelicotlp/describe.go index ad16d99d5..4831ceea6 100644 --- a/pkg/commands/logging/newrelicotlp/describe.go +++ b/pkg/commands/logging/newrelicotlp/describe.go @@ -3,21 +3,20 @@ package newrelicotlp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Get the details of a New Relic OTLP Logs logging object for a particular service and version").Alias("get") @@ -35,7 +34,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -68,7 +66,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelicotlp/list.go b/pkg/commands/logging/newrelicotlp/list.go index e580b293b..205a75e2e 100644 --- a/pkg/commands/logging/newrelicotlp/list.go +++ b/pkg/commands/logging/newrelicotlp/list.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List all of the New Relic OTLP Logs logging objects for a particular service and version") @@ -35,7 +34,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go index 755397624..135fe50aa 100644 --- a/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go +++ b/pkg/commands/logging/newrelicotlp/newrelicotlp_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -76,8 +77,8 @@ func TestNewRelicOTLPCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -154,8 +155,8 @@ func TestNewRelicOTLPDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -219,8 +220,8 @@ func TestNewRelicDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -288,8 +289,8 @@ func TestNewRelicList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -374,8 +375,8 @@ func TestNewRelicUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/logging/newrelicotlp/update.go b/pkg/commands/logging/newrelicotlp/update.go index f364373b7..72e09dfdf 100644 --- a/pkg/commands/logging/newrelicotlp/update.go +++ b/pkg/commands/logging/newrelicotlp/update.go @@ -4,13 +4,13 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an appropriate resource. @@ -25,7 +25,6 @@ type UpdateCommand struct { format cmd.OptionalString formatVersion cmd.OptionalInt key cmd.OptionalString - manifest manifest.Data newName cmd.OptionalString placement cmd.OptionalString region cmd.OptionalString @@ -34,12 +33,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a New Relic Logs logging object for a particular service and version") @@ -68,7 +66,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -86,7 +84,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/openstack/create.go b/pkg/commands/logging/openstack/create.go index ec7582917..3b09ac924 100644 --- a/pkg/commands/logging/openstack/create.go +++ b/pkg/commands/logging/openstack/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an OpenStack logging endpoint. @@ -43,12 +44,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an OpenStack logging endpoint on a Fastly service version").Alias("add") @@ -82,7 +82,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -176,7 +176,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/openstack/delete.go b/pkg/commands/logging/openstack/delete.go index c181d7142..2aad45bb2 100644 --- a/pkg/commands/logging/openstack/delete.go +++ b/pkg/commands/logging/openstack/delete.go @@ -3,18 +3,17 @@ package openstack import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an OpenStack logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteOpenstackInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an OpenStack logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/openstack/describe.go b/pkg/commands/logging/openstack/describe.go index ed95f271a..5f5c79d63 100644 --- a/pkg/commands/logging/openstack/describe.go +++ b/pkg/commands/logging/openstack/describe.go @@ -3,12 +3,12 @@ package openstack import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an OpenStack logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetOpenstackInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about an OpenStack logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/openstack/list.go b/pkg/commands/logging/openstack/list.go index 12f145140..f31be5e56 100644 --- a/pkg/commands/logging/openstack/list.go +++ b/pkg/commands/logging/openstack/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list OpenStack logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListOpenstackInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List OpenStack logging endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/openstack/openstack_integration_test.go b/pkg/commands/logging/openstack/openstack_integration_test.go index e61061a6d..309356b44 100644 --- a/pkg/commands/logging/openstack/openstack_integration_test.go +++ b/pkg/commands/logging/openstack/openstack_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -53,8 +54,8 @@ func TestOpenstackCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -126,8 +127,8 @@ func TestOpenstackList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestOpenstackDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -218,8 +219,8 @@ func TestOpenstackUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -265,8 +266,8 @@ func TestOpenstackDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/openstack/update.go b/pkg/commands/logging/openstack/update.go index d342d314e..6e3bce1bc 100644 --- a/pkg/commands/logging/openstack/update.go +++ b/pkg/commands/logging/openstack/update.go @@ -3,13 +3,14 @@ package openstack import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an OpenStack logging endpoint. @@ -43,12 +44,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update an OpenStack logging endpoint on a Fastly service version") @@ -82,7 +82,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -179,7 +179,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/papertrail/create.go b/pkg/commands/logging/papertrail/create.go index c0c72a10a..ed54e2e53 100644 --- a/pkg/commands/logging/papertrail/create.go +++ b/pkg/commands/logging/papertrail/create.go @@ -3,13 +3,14 @@ package papertrail import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Papertrail logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Papertrail logging endpoint on a Fastly service version").Alias("add") @@ -65,7 +65,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -118,7 +118,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/papertrail/delete.go b/pkg/commands/logging/papertrail/delete.go index a6254aa8c..664905f45 100644 --- a/pkg/commands/logging/papertrail/delete.go +++ b/pkg/commands/logging/papertrail/delete.go @@ -3,18 +3,17 @@ package papertrail import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Papertrail logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeletePapertrailInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Papertrail logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/papertrail/describe.go b/pkg/commands/logging/papertrail/describe.go index 153f8d962..a9a51c12d 100644 --- a/pkg/commands/logging/papertrail/describe.go +++ b/pkg/commands/logging/papertrail/describe.go @@ -3,12 +3,12 @@ package papertrail import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Papertrail logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetPapertrailInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Papertrail logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/papertrail/list.go b/pkg/commands/logging/papertrail/list.go index 3c63dc779..67bade01e 100644 --- a/pkg/commands/logging/papertrail/list.go +++ b/pkg/commands/logging/papertrail/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Papertrail logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListPapertrailsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Papertrail endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/papertrail/papertrail_integration_test.go b/pkg/commands/logging/papertrail/papertrail_integration_test.go index 2e153eb59..11c2c772d 100644 --- a/pkg/commands/logging/papertrail/papertrail_integration_test.go +++ b/pkg/commands/logging/papertrail/papertrail_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestPapertrailCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestPapertrailList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestPapertrailDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestPapertrailUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestPapertrailDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/papertrail/update.go b/pkg/commands/logging/papertrail/update.go index 14d30e662..2686fab7a 100644 --- a/pkg/commands/logging/papertrail/update.go +++ b/pkg/commands/logging/papertrail/update.go @@ -3,13 +3,14 @@ package papertrail import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Papertrail logging endpoint. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Papertrail logging endpoint on a Fastly service version") @@ -67,7 +67,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -128,7 +128,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/s3/create.go b/pkg/commands/logging/s3/create.go index 6d51c45a5..69fe4e953 100644 --- a/pkg/commands/logging/s3/create.go +++ b/pkg/commands/logging/s3/create.go @@ -52,12 +52,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an Amazon S3 logging endpoint on a Fastly service version").Alias("add") @@ -97,7 +96,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -266,7 +265,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/s3/delete.go b/pkg/commands/logging/s3/delete.go index d299411e8..ccfc12ea2 100644 --- a/pkg/commands/logging/s3/delete.go +++ b/pkg/commands/logging/s3/delete.go @@ -3,18 +3,17 @@ package s3 import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an Amazon S3 logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteS3Input serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a S3 logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/s3/describe.go b/pkg/commands/logging/s3/describe.go index 170175f8b..f832f02b6 100644 --- a/pkg/commands/logging/s3/describe.go +++ b/pkg/commands/logging/s3/describe.go @@ -3,12 +3,12 @@ package s3 import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an Amazon S3 logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetS3Input serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a S3 logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/s3/list.go b/pkg/commands/logging/s3/list.go index 39367bdac..85586b9a5 100644 --- a/pkg/commands/logging/s3/list.go +++ b/pkg/commands/logging/s3/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Amazon S3 logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListS3sInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List S3 endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/s3/s3_integration_test.go b/pkg/commands/logging/s3/s3_integration_test.go index ef1f340e3..6c94092a5 100644 --- a/pkg/commands/logging/s3/s3_integration_test.go +++ b/pkg/commands/logging/s3/s3_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -103,8 +104,8 @@ func TestS3Create(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -176,8 +177,8 @@ func TestS3List(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -221,8 +222,8 @@ func TestS3Describe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -277,8 +278,8 @@ func TestS3Update(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -324,8 +325,8 @@ func TestS3Delete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/s3/update.go b/pkg/commands/logging/s3/update.go index 991a0e10f..9c64f4d47 100644 --- a/pkg/commands/logging/s3/update.go +++ b/pkg/commands/logging/s3/update.go @@ -3,13 +3,14 @@ package s3 import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an Amazon S3 logging endpoint. @@ -49,12 +50,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a S3 logging endpoint on a Fastly service version") @@ -95,7 +95,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -216,7 +216,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/scalyr/create.go b/pkg/commands/logging/scalyr/create.go index 77d96afe4..e1e54e517 100644 --- a/pkg/commands/logging/scalyr/create.go +++ b/pkg/commands/logging/scalyr/create.go @@ -3,13 +3,14 @@ package scalyr import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Scalyr logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Scalyr logging endpoint on a Fastly service version").Alias("add") @@ -65,7 +65,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -118,7 +118,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/scalyr/delete.go b/pkg/commands/logging/scalyr/delete.go index bd1cceb1d..90178aa63 100644 --- a/pkg/commands/logging/scalyr/delete.go +++ b/pkg/commands/logging/scalyr/delete.go @@ -3,18 +3,17 @@ package scalyr import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Scalyr logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteScalyrInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Scalyr logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/scalyr/describe.go b/pkg/commands/logging/scalyr/describe.go index 2a99bfcd1..5ec914c2b 100644 --- a/pkg/commands/logging/scalyr/describe.go +++ b/pkg/commands/logging/scalyr/describe.go @@ -3,12 +3,12 @@ package scalyr import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Scalyr logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetScalyrInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Scalyr logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/scalyr/list.go b/pkg/commands/logging/scalyr/list.go index 287602c60..9d9fb274c 100644 --- a/pkg/commands/logging/scalyr/list.go +++ b/pkg/commands/logging/scalyr/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Scalyr logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListScalyrsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Scalyr endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/scalyr/scalyr_integration_test.go b/pkg/commands/logging/scalyr/scalyr_integration_test.go index 530d6e3dc..df0669554 100644 --- a/pkg/commands/logging/scalyr/scalyr_integration_test.go +++ b/pkg/commands/logging/scalyr/scalyr_integration_test.go @@ -11,6 +11,7 @@ import ( "github.com/fastly/cli/pkg/app" fsterrs "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -54,8 +55,8 @@ func TestScalyrCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -127,8 +128,8 @@ func TestScalyrList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -172,8 +173,8 @@ func TestScalyrDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -219,8 +220,8 @@ func TestScalyrUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -266,8 +267,8 @@ func TestScalyrDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/scalyr/update.go b/pkg/commands/logging/scalyr/update.go index 8ffc6ded5..e5b1300cb 100644 --- a/pkg/commands/logging/scalyr/update.go +++ b/pkg/commands/logging/scalyr/update.go @@ -3,13 +3,14 @@ package scalyr import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update Scalyr logging endpoints. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Scalyr logging endpoint on a Fastly service version") @@ -67,7 +67,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -123,7 +123,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/sftp/create.go b/pkg/commands/logging/sftp/create.go index 7fbdce271..1519eaae5 100644 --- a/pkg/commands/logging/sftp/create.go +++ b/pkg/commands/logging/sftp/create.go @@ -4,13 +4,14 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create an SFTP logging endpoint. @@ -45,12 +46,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create an SFTP logging endpoint on a Fastly service version").Alias("add") @@ -85,7 +85,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -189,7 +189,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/sftp/delete.go b/pkg/commands/logging/sftp/delete.go index 125162138..da6af0aea 100644 --- a/pkg/commands/logging/sftp/delete.go +++ b/pkg/commands/logging/sftp/delete.go @@ -3,18 +3,17 @@ package sftp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an SFTP logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteSFTPInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete an SFTP logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/sftp/describe.go b/pkg/commands/logging/sftp/describe.go index ad28b0aa0..086329891 100644 --- a/pkg/commands/logging/sftp/describe.go +++ b/pkg/commands/logging/sftp/describe.go @@ -3,12 +3,12 @@ package sftp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe an SFTP logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetSFTPInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about an SFTP logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/sftp/list.go b/pkg/commands/logging/sftp/list.go index 73111719c..24d6faaa0 100644 --- a/pkg/commands/logging/sftp/list.go +++ b/pkg/commands/logging/sftp/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list SFTP logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListSFTPsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List SFTP endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/sftp/sftp_integration_test.go b/pkg/commands/logging/sftp/sftp_integration_test.go index 7efeddda2..f4757fbd3 100644 --- a/pkg/commands/logging/sftp/sftp_integration_test.go +++ b/pkg/commands/logging/sftp/sftp_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -53,8 +54,8 @@ func TestSFTPCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -126,8 +127,8 @@ func TestSFTPList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestSFTPDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -218,8 +219,8 @@ func TestSFTPUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -265,8 +266,8 @@ func TestSFTPDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/sftp/update.go b/pkg/commands/logging/sftp/update.go index 686f26679..5d4c0c970 100644 --- a/pkg/commands/logging/sftp/update.go +++ b/pkg/commands/logging/sftp/update.go @@ -3,13 +3,14 @@ package sftp import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update an SFTP logging endpoint. @@ -45,12 +46,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update an SFTP logging endpoint on a Fastly service version") @@ -86,7 +86,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -189,7 +189,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/splunk/create.go b/pkg/commands/logging/splunk/create.go index b0bed74d6..aea271774 100644 --- a/pkg/commands/logging/splunk/create.go +++ b/pkg/commands/logging/splunk/create.go @@ -3,13 +3,14 @@ package splunk import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Splunk logging endpoint. @@ -38,12 +39,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Splunk logging endpoint on a Fastly service version").Alias("add") @@ -69,7 +69,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -143,7 +143,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/splunk/delete.go b/pkg/commands/logging/splunk/delete.go index 796c16fdc..3f8b2f3cf 100644 --- a/pkg/commands/logging/splunk/delete.go +++ b/pkg/commands/logging/splunk/delete.go @@ -3,18 +3,17 @@ package splunk import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Splunk logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteSplunkInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Splunk logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/splunk/describe.go b/pkg/commands/logging/splunk/describe.go index 1aef369e5..e99554cf4 100644 --- a/pkg/commands/logging/splunk/describe.go +++ b/pkg/commands/logging/splunk/describe.go @@ -3,12 +3,12 @@ package splunk import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Splunk logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetSplunkInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Splunk logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/splunk/list.go b/pkg/commands/logging/splunk/list.go index 81a81f7d5..a89e92f39 100644 --- a/pkg/commands/logging/splunk/list.go +++ b/pkg/commands/logging/splunk/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Splunk logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListSplunksInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Splunk endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/splunk/splunk_integration_test.go b/pkg/commands/logging/splunk/splunk_integration_test.go index 21852b186..e86768228 100644 --- a/pkg/commands/logging/splunk/splunk_integration_test.go +++ b/pkg/commands/logging/splunk/splunk_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestSplunkCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestSplunkList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestSplunkDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestSplunkUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestSplunkDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/splunk/update.go b/pkg/commands/logging/splunk/update.go index 46a4795ac..41f100bb6 100644 --- a/pkg/commands/logging/splunk/update.go +++ b/pkg/commands/logging/splunk/update.go @@ -3,13 +3,14 @@ package splunk import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Splunk logging endpoint. @@ -38,12 +39,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Splunk logging endpoint on a Fastly service version") @@ -70,7 +70,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -148,7 +148,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/sumologic/create.go b/pkg/commands/logging/sumologic/create.go index fba3c5761..6cd0d1b97 100644 --- a/pkg/commands/logging/sumologic/create.go +++ b/pkg/commands/logging/sumologic/create.go @@ -3,13 +3,14 @@ package sumologic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Sumologic logging endpoint. @@ -33,12 +34,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Sumologic logging endpoint on a Fastly service version").Alias("add") @@ -64,7 +64,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -118,7 +118,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/sumologic/delete.go b/pkg/commands/logging/sumologic/delete.go index 38aec8568..568a6f6c1 100644 --- a/pkg/commands/logging/sumologic/delete.go +++ b/pkg/commands/logging/sumologic/delete.go @@ -3,18 +3,17 @@ package sumologic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Sumologic logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteSumologicInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Sumologic logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/sumologic/describe.go b/pkg/commands/logging/sumologic/describe.go index 12cdc65e4..54432c617 100644 --- a/pkg/commands/logging/sumologic/describe.go +++ b/pkg/commands/logging/sumologic/describe.go @@ -3,12 +3,12 @@ package sumologic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Sumologic logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetSumologicInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Sumologic logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/sumologic/list.go b/pkg/commands/logging/sumologic/list.go index 4a088f9bf..8734beedd 100644 --- a/pkg/commands/logging/sumologic/list.go +++ b/pkg/commands/logging/sumologic/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Sumologic logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListSumologicsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Sumologic endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/sumologic/sumologic_integration_test.go b/pkg/commands/logging/sumologic/sumologic_integration_test.go index 73256ba2b..62bf6e99c 100644 --- a/pkg/commands/logging/sumologic/sumologic_integration_test.go +++ b/pkg/commands/logging/sumologic/sumologic_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestSumologicCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestSumologicList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestSumologicDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestSumologicUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestSumologicDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/logging/sumologic/update.go b/pkg/commands/logging/sumologic/update.go index cc1fcc442..ba9937ece 100644 --- a/pkg/commands/logging/sumologic/update.go +++ b/pkg/commands/logging/sumologic/update.go @@ -3,13 +3,14 @@ package sumologic import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Sumologic logging endpoint. @@ -34,12 +35,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Sumologic logging endpoint on a Fastly service version") @@ -66,7 +66,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -124,7 +124,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/syslog/create.go b/pkg/commands/logging/syslog/create.go index e53fa93b4..8819355c5 100644 --- a/pkg/commands/logging/syslog/create.go +++ b/pkg/commands/logging/syslog/create.go @@ -3,13 +3,14 @@ package syslog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a Syslog logging endpoint. @@ -40,12 +41,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("create", "Create a Syslog logging endpoint on a Fastly service version").Alias("add") @@ -68,7 +68,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) common.Format(c.CmdClause, &c.Format) @@ -159,7 +159,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logging/syslog/delete.go b/pkg/commands/logging/syslog/delete.go index 9e7f5f143..09fbab3b5 100644 --- a/pkg/commands/logging/syslog/delete.go +++ b/pkg/commands/logging/syslog/delete.go @@ -3,18 +3,17 @@ package syslog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete a Syslog logging endpoint. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteSyslogInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Syslog logging endpoint on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -65,7 +63,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/syslog/describe.go b/pkg/commands/logging/syslog/describe.go index e6be17ad4..a96c0e2de 100644 --- a/pkg/commands/logging/syslog/describe.go +++ b/pkg/commands/logging/syslog/describe.go @@ -3,12 +3,12 @@ package syslog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a Syslog logging endpoint. @@ -16,19 +16,17 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetSyslogInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Syslog logging endpoint on a Fastly service version").Alias("get") @@ -46,7 +44,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/syslog/list.go b/pkg/commands/logging/syslog/list.go index 8df31e5e0..72e140896 100644 --- a/pkg/commands/logging/syslog/list.go +++ b/pkg/commands/logging/syslog/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list Syslog logging endpoints. @@ -17,19 +17,17 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListSyslogsInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Syslog endpoints on a Fastly service version") @@ -46,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/logging/syslog/syslog_integration_test.go b/pkg/commands/logging/syslog/syslog_integration_test.go index 9527851fc..311a807e3 100644 --- a/pkg/commands/logging/syslog/syslog_integration_test.go +++ b/pkg/commands/logging/syslog/syslog_integration_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestSyslogCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -118,8 +119,8 @@ func TestSyslogList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -163,8 +164,8 @@ func TestSyslogDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -210,8 +211,8 @@ func TestSyslogUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -257,8 +258,8 @@ func TestSyslogDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -290,7 +291,7 @@ func listSyslogsOK(i *fastly.ListSyslogsInput) ([]*fastly.Syslog, error) { ServiceVersion: i.ServiceVersion, Name: "logs", Address: "127.0.0.1", - Hostname: "", + Hostname: "127.0.0.1", Port: 514, UseTLS: false, IPV4: "127.0.0.1", @@ -313,7 +314,7 @@ func listSyslogsOK(i *fastly.ListSyslogsInput) ([]*fastly.Syslog, error) { Hostname: "example.com", Port: 789, UseTLS: true, - IPV4: "", + IPV4: "127.0.0.1", TLSCACert: "-----BEGIN CERTIFICATE-----baz", TLSHostname: "example.com", TLSClientCert: "-----BEGIN CERTIFICATE-----qux", @@ -350,7 +351,7 @@ Version: 1 Version: 1 Name: logs Address: 127.0.0.1 - Hostname: + Hostname: 127.0.0.1 Port: 514 Use TLS: false IPV4: 127.0.0.1 @@ -372,7 +373,7 @@ Version: 1 Hostname: example.com Port: 789 Use TLS: true - IPV4: + IPV4: 127.0.0.1 TLS CA certificate: -----BEGIN CERTIFICATE-----baz TLS hostname: example.com TLS client certificate: -----BEGIN CERTIFICATE-----qux diff --git a/pkg/commands/logging/syslog/update.go b/pkg/commands/logging/syslog/update.go index 5c406fa02..155a7a91d 100644 --- a/pkg/commands/logging/syslog/update.go +++ b/pkg/commands/logging/syslog/update.go @@ -3,13 +3,14 @@ package syslog import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/commands/logging/common" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a Syslog logging endpoint. @@ -41,12 +42,11 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - Manifest: m, } c.CmdClause = parent.Command("update", "Update a Syslog logging endpoint on a Fastly service version") @@ -75,7 +75,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.Manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -166,7 +166,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.AutoClone, APIClient: c.Globals.APIClient, - Manifest: c.Manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.ServiceName, ServiceVersionFlag: c.ServiceVersion, diff --git a/pkg/commands/logtail/root.go b/pkg/commands/logtail/root.go index 261300273..98228ea71 100644 --- a/pkg/commands/logtail/root.go +++ b/pkg/commands/logtail/root.go @@ -22,7 +22,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -37,21 +36,19 @@ type RootCommand struct { dieCh chan struct{} // channel to end output/printing doneCh chan struct{} // channel to signal we've reached the end of the run hClient *http.Client // TODO: this will go away when GET is in go-fastly - manifest manifest.Data serviceName cmd.OptionalServiceNameID token string // TODO: this will go away when GET is in go-fastly } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("log-tail", "Tail Compute logs") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -70,7 +67,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Roo // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/pop/pop_test.go b/pkg/commands/pop/pop_test.go index 416837b08..faaa3751f 100644 --- a/pkg/commands/pop/pop_test.go +++ b/pkg/commands/pop/pop_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -33,8 +34,8 @@ func TestAllDatacenters(t *testing.T) { }, nil }, } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(args, &stdout) opts.APIClientFactory = mock.APIClient(api) return opts, nil } diff --git a/pkg/commands/products/products_test.go b/pkg/commands/products/products_test.go index 472b5ceef..cb7689806 100644 --- a/pkg/commands/products/products_test.go +++ b/pkg/commands/products/products_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -134,8 +135,8 @@ Web Sockets true testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/products/root.go b/pkg/commands/products/root.go index ec2b13390..052a832e5 100644 --- a/pkg/commands/products/root.go +++ b/pkg/commands/products/root.go @@ -10,7 +10,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -22,7 +21,6 @@ type RootCommand struct { disableProduct string enableProduct string - manifest manifest.Data serviceName cmd.OptionalServiceNameID } @@ -37,10 +35,9 @@ var ProductEnablementOptions = []string{ } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("products", "Enable, disable, and check the enablement status of products") // Optional. @@ -50,7 +47,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Roo c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -74,7 +71,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, _, _, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, _, _, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return fmt.Errorf("failed to identify Service ID: %w", err) } diff --git a/pkg/commands/profile/create.go b/pkg/commands/profile/create.go index eea8b8110..a177b8c83 100644 --- a/pkg/commands/profile/create.go +++ b/pkg/commands/profile/create.go @@ -27,13 +27,12 @@ type CreateCommand struct { authCmd *sso.RootCommand automationToken bool - clientFactory APIClientFactory profile string sso bool } // NewCreateCommand returns a new command registered in the parent. -func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *sso.RootCommand) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data, authCmd *sso.RootCommand) *CreateCommand { var c CreateCommand c.Globals = g c.authCmd = authCmd @@ -41,7 +40,6 @@ func NewCreateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data c.CmdClause.Arg("profile", "Profile to create (default 'user')").Default(profile.DefaultName).Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) c.CmdClause.Flag("sso", "Create an SSO-based token").Hidden().BoolVar(&c.sso) - c.clientFactory = cf return &c } @@ -164,7 +162,7 @@ func (c *CreateCommand) validateToken(token, endpoint string, spinner text.Spinn t *fastly.Token ) err = spinner.Process("Validating token", func(_ *text.SpinnerWrapper) error { - client, err = c.clientFactory(token, endpoint, c.Globals.Flags.Debug) + client, err = c.Globals.APIClientFactory(token, endpoint, c.Globals.Flags.Debug) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ "Endpoint": endpoint, diff --git a/pkg/commands/profile/profile_test.go b/pkg/commands/profile/profile_test.go index 596a36bcd..3f5a6f7f1 100644 --- a/pkg/commands/profile/profile_test.go +++ b/pkg/commands/profile/profile_test.go @@ -14,6 +14,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/config" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -113,7 +114,7 @@ func TestCreate(t *testing.T) { stdout bytes.Buffer ) - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over @@ -123,7 +124,7 @@ func TestCreate(t *testing.T) { // The read of the config file only really happens in the main() // function, so for the sake of the test environment we need to construct // an in-memory representation of the config file we want to be using. - opts.ConfigFile = testcase.ConfigFile + opts.Config = testcase.ConfigFile // TODO: abstract the logic for handling interactive stdin prompts. // This same if/else block is fundamentally duplicated across test files. @@ -131,7 +132,7 @@ func TestCreate(t *testing.T) { // To handle multiple prompt input from the user we need to do some // coordination around io pipes to mimic the required user behaviour. stdin, prompt := io.Pipe() - opts.Stdin = stdin + opts.Input = stdin // Wait for user input and write it to the prompt inputc := make(chan string) @@ -146,7 +147,7 @@ func TestCreate(t *testing.T) { // Call `app.Run()` and wait for response go func() { - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -173,8 +174,8 @@ func TestCreate(t *testing.T) { if len(testcase.Stdin) > 0 { stdin = testcase.Stdin[0] } - opts.Stdin = strings.NewReader(stdin) - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.Input = strings.NewReader(stdin) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -264,7 +265,7 @@ func TestDelete(t *testing.T) { stdout bytes.Buffer ) - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over @@ -274,9 +275,9 @@ func TestDelete(t *testing.T) { // The read of the config file only really happens in the main() // function, so for the sake of the test environment we need to construct // an in-memory representation of the config file we want to be using. - opts.ConfigFile = testcase.ConfigFile + opts.Config = testcase.ConfigFile - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -481,7 +482,7 @@ func TestList(t *testing.T) { stdout bytes.Buffer ) - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over @@ -491,9 +492,9 @@ func TestList(t *testing.T) { // The read of the config file only really happens in the main() // function, so for the sake of the test environment we need to construct // an in-memory representation of the config file we want to be using. - opts.ConfigFile = testcase.ConfigFile + opts.Config = testcase.ConfigFile - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -587,7 +588,7 @@ func TestSwitch(t *testing.T) { stdout bytes.Buffer ) - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over @@ -597,9 +598,9 @@ func TestSwitch(t *testing.T) { // The read of the config file only really happens in the main() // function, so for the sake of the test environment we need to construct // an in-memory representation of the config file we want to be using. - opts.ConfigFile = testcase.ConfigFile + opts.Config = testcase.ConfigFile - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -735,7 +736,7 @@ func TestToken(t *testing.T) { stdout bytes.Buffer ) - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over @@ -745,9 +746,9 @@ func TestToken(t *testing.T) { // The read of the config file only really happens in the main() // function, so for the sake of the test environment we need to construct // an in-memory representation of the config file we want to be using. - opts.ConfigFile = testcase.ConfigFile + opts.Config = testcase.ConfigFile - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -850,7 +851,7 @@ func TestUpdate(t *testing.T) { stdout bytes.Buffer ) - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) // We override the config path so that we don't accidentally write over @@ -860,13 +861,13 @@ func TestUpdate(t *testing.T) { // The read of the config file only really happens in the main() // function, so for the sake of the test environment we need to construct // an in-memory representation of the config file we want to be using. - opts.ConfigFile = testcase.ConfigFile + opts.Config = testcase.ConfigFile if len(testcase.Stdin) > 1 { // To handle multiple prompt input from the user we need to do some // coordination around io pipes to mimic the required user behaviour. stdin, prompt := io.Pipe() - opts.Stdin = stdin + opts.Input = stdin // Wait for user input and write it to the prompt inputc := make(chan string) @@ -881,7 +882,7 @@ func TestUpdate(t *testing.T) { // Call `app.Run()` and wait for response go func() { - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -908,8 +909,8 @@ func TestUpdate(t *testing.T) { if len(testcase.Stdin) > 0 { stdin = testcase.Stdin[0] } - opts.Stdin = strings.NewReader(stdin) - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.Input = strings.NewReader(stdin) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) diff --git a/pkg/commands/profile/update.go b/pkg/commands/profile/update.go index b5d17e472..81ffe6802 100644 --- a/pkg/commands/profile/update.go +++ b/pkg/commands/profile/update.go @@ -28,13 +28,12 @@ type UpdateCommand struct { authCmd *sso.RootCommand automationToken bool - clientFactory APIClientFactory profile string sso bool } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data, authCmd *sso.RootCommand) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data, authCmd *sso.RootCommand) *UpdateCommand { var c UpdateCommand c.Globals = g c.authCmd = authCmd @@ -42,7 +41,6 @@ func NewUpdateCommand(parent cmd.Registerer, cf APIClientFactory, g *global.Data c.CmdClause.Arg("profile", "Profile to update (defaults to the currently active profile)").Short('p').StringVar(&c.profile) c.CmdClause.Flag("automation-token", "Expected input will be an 'automation token' instead of a 'user token'").BoolVar(&c.automationToken) c.CmdClause.Flag("sso", "Update profile to use an SSO-based token").Hidden().BoolVar(&c.sso) - c.clientFactory = cf return &c } @@ -176,7 +174,7 @@ func (c *UpdateCommand) validateToken(token, endpoint string, spinner text.Spinn t *fastly.Token ) err = spinner.Process("Validating token", func(_ *text.SpinnerWrapper) error { - client, err = c.clientFactory(token, endpoint, c.Globals.Flags.Debug) + client, err = c.Globals.APIClientFactory(token, endpoint, c.Globals.Flags.Debug) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ "Endpoint": endpoint, diff --git a/pkg/commands/purge/purge_test.go b/pkg/commands/purge/purge_test.go index a0f92969e..c758765e5 100644 --- a/pkg/commands/purge/purge_test.go +++ b/pkg/commands/purge/purge_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -54,8 +55,8 @@ func TestPurgeAll(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -108,8 +109,8 @@ func TestPurgeKeys(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -188,8 +189,8 @@ func TestPurgeKey(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -246,8 +247,8 @@ func TestPurgeURL(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/purge/root.go b/pkg/commands/purge/root.go index 5addaaae9..c0ca73fd7 100644 --- a/pkg/commands/purge/root.go +++ b/pkg/commands/purge/root.go @@ -18,11 +18,10 @@ import ( ) // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand c.CmdClause = parent.Command("purge", "Invalidate objects in the Fastly cache") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("all", "Purge everything from a service").BoolVar(&c.all) @@ -31,7 +30,7 @@ func NewRootCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Roo c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -54,7 +53,6 @@ type RootCommand struct { all bool file string key string - manifest manifest.Data serviceName cmd.OptionalServiceNameID soft bool url string @@ -62,7 +60,7 @@ type RootCommand struct { // Exec implements the command interface. func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/ratelimit/create.go b/pkg/commands/ratelimit/create.go index 229748385..b6cc6e672 100644 --- a/pkg/commands/ratelimit/create.go +++ b/pkg/commands/ratelimit/create.go @@ -11,7 +11,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -43,12 +42,11 @@ var rateLimitWindowSizeFlagOpts = func() (windowSizes []string) { }() // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a rate limiter for a particular service and version").Alias("add") @@ -82,7 +80,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -108,7 +106,6 @@ type CreateCommand struct { featRevision int httpMethods string loggerType string - manifest manifest.Data name string penaltyDuration int responseContent string @@ -138,7 +135,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/ratelimit/delete.go b/pkg/commands/ratelimit/delete.go index 118e5a714..68ed9f054 100644 --- a/pkg/commands/ratelimit/delete.go +++ b/pkg/commands/ratelimit/delete.go @@ -7,16 +7,14 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, globals *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("delete", "Delete a rate limiter by its ID").Alias("remove") c.Globals = globals - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying the rate limiter").Required().StringVar(&c.id) @@ -28,8 +26,7 @@ func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, m manifest.Da type DeleteCommand struct { cmd.Base - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/ratelimit/describe.go b/pkg/commands/ratelimit/describe.go index 608d5a3a8..cc648f94e 100644 --- a/pkg/commands/ratelimit/describe.go +++ b/pkg/commands/ratelimit/describe.go @@ -9,15 +9,13 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Get a rate limiter by its ID").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying the rate limiter").Required().StringVar(&c.id) @@ -33,8 +31,7 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/ratelimit/list.go b/pkg/commands/ratelimit/list.go index dd31824b4..c3161b180 100644 --- a/pkg/commands/ratelimit/list.go +++ b/pkg/commands/ratelimit/list.go @@ -9,16 +9,14 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all rate limiters for a particular service and version") c.Globals = g - c.manifest = m // Required. c.RegisterFlag(cmd.StringFlagOpts{ @@ -33,7 +31,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -50,7 +48,6 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -64,7 +61,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/ratelimit/ratelimit_test.go b/pkg/commands/ratelimit/ratelimit_test.go index fc2553c3d..a14bbc64e 100644 --- a/pkg/commands/ratelimit/ratelimit_test.go +++ b/pkg/commands/ratelimit/ratelimit_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -46,8 +47,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -87,8 +88,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -135,8 +136,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -187,8 +188,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -231,8 +232,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/ratelimit/update.go b/pkg/commands/ratelimit/update.go index 74421ae4f..c4a9d3b2b 100644 --- a/pkg/commands/ratelimit/update.go +++ b/pkg/commands/ratelimit/update.go @@ -11,17 +11,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a rate limiter by its ID") @@ -60,7 +58,6 @@ type UpdateCommand struct { httpMethods string id string loggerType string - manifest manifest.Data name string penaltyDuration int responseContent string diff --git a/pkg/commands/resourcelink/create.go b/pkg/commands/resourcelink/create.go index e30876df1..11cb0b3d9 100644 --- a/pkg/commands/resourcelink/create.go +++ b/pkg/commands/resourcelink/create.go @@ -3,12 +3,12 @@ package resourcelink import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create a resource link. @@ -18,18 +18,16 @@ type CreateCommand struct { autoClone cmd.OptionalAutoClone input fastly.CreateResourceInput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, input: fastly.CreateResourceInput{ // Kingpin requires the following to be initialized. ResourceID: new(string), @@ -58,7 +56,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, }) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceName, @@ -92,7 +90,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, @@ -100,7 +98,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { }) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "Service ID": c.manifest.Flag.ServiceID, + "Service ID": c.Globals.Manifest.Flag.ServiceID, "Service Version": fsterr.ServiceVersion(serviceVersion), }) return err diff --git a/pkg/commands/resourcelink/delete.go b/pkg/commands/resourcelink/delete.go index ec1c7a228..fee8b6c3b 100644 --- a/pkg/commands/resourcelink/delete.go +++ b/pkg/commands/resourcelink/delete.go @@ -3,12 +3,12 @@ package resourcelink import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete service resource links. @@ -18,18 +18,16 @@ type DeleteCommand struct { autoClone cmd.OptionalAutoClone input fastly.DeleteResourceInput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a resource link for a Fastly service version").Alias("remove") @@ -52,7 +50,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, }) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceName, @@ -80,7 +78,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, @@ -88,7 +86,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { }) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "Service ID": c.manifest.Flag.ServiceID, + "Service ID": c.Globals.Manifest.Flag.ServiceID, "Service Version": fsterr.ServiceVersion(serviceVersion), }) return err diff --git a/pkg/commands/resourcelink/describe.go b/pkg/commands/resourcelink/describe.go index 9c460f8c9..04871dd52 100644 --- a/pkg/commands/resourcelink/describe.go +++ b/pkg/commands/resourcelink/describe.go @@ -3,12 +3,12 @@ package resourcelink import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a service resource link. @@ -17,18 +17,16 @@ type DescribeCommand struct { cmd.JSONOutput input fastly.GetResourceInput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Fastly service resource link").Alias("get") @@ -51,7 +49,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, }) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceName, @@ -72,7 +70,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/resourcelink/list.go b/pkg/commands/resourcelink/list.go index c5f64ec08..46f91183d 100644 --- a/pkg/commands/resourcelink/list.go +++ b/pkg/commands/resourcelink/list.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -19,18 +18,16 @@ type ListCommand struct { cmd.JSONOutput input fastly.ListResourcesInput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List all resource links for a Fastly service version") @@ -47,7 +44,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, }) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceName, @@ -68,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/resourcelink/resourcelink_test.go b/pkg/commands/resourcelink/resourcelink_test.go index 9cf23064c..6a046f579 100644 --- a/pkg/commands/resourcelink/resourcelink_test.go +++ b/pkg/commands/resourcelink/resourcelink_test.go @@ -12,6 +12,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/resourcelink" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -147,7 +148,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(resourcelink.RootName + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.CreateResourceFn var apiInvoked bool @@ -156,7 +157,7 @@ func TestCreateServiceResourceCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -252,7 +253,7 @@ func TestDeleteServiceResourceCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(resourcelink.RootName + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.DeleteResourceFn var apiInvoked bool @@ -261,7 +262,7 @@ func TestDeleteServiceResourceCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -349,7 +350,7 @@ Last edited (UTC): 2023-10-15 12:18`, t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(resourcelink.RootName + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.GetResourceFn var apiInvoked bool @@ -358,7 +359,7 @@ Last edited (UTC): 2023-10-15 12:18`, return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -464,7 +465,7 @@ Resource Link 3/3 t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(resourcelink.RootName + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.ListResourcesFn var apiInvoked bool @@ -473,7 +474,7 @@ Resource Link 3/3 return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -602,7 +603,7 @@ func TestUpdateServiceResourceCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(resourcelink.RootName + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.UpdateResourceFn var apiInvoked bool @@ -611,7 +612,7 @@ func TestUpdateServiceResourceCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/resourcelink/update.go b/pkg/commands/resourcelink/update.go index 5c8d1fca5..c83673452 100644 --- a/pkg/commands/resourcelink/update.go +++ b/pkg/commands/resourcelink/update.go @@ -3,12 +3,12 @@ package resourcelink import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update a dictionary. @@ -18,18 +18,16 @@ type UpdateCommand struct { autoClone cmd.OptionalAutoClone input fastly.UpdateResourceInput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, input: fastly.UpdateResourceInput{ // Kingpin requires the following to be initialized. Name: new(string), @@ -63,7 +61,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U Name: cmd.FlagServiceIDName, Short: 's', Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, }) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceName, @@ -91,7 +89,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, @@ -99,7 +97,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { }) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "Service ID": c.manifest.Flag.ServiceID, + "Service ID": c.Globals.Manifest.Flag.ServiceID, "Service Version": fsterr.ServiceVersion(serviceVersion), }) return err diff --git a/pkg/commands/secretstore/create.go b/pkg/commands/secretstore/create.go index 05f3ac448..26b14129e 100644 --- a/pkg/commands/secretstore/create.go +++ b/pkg/commands/secretstore/create.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a new secret store") @@ -37,8 +35,7 @@ type CreateCommand struct { cmd.Base cmd.JSONOutput - Input fastly.CreateSecretStoreInput - manifest manifest.Data + Input fastly.CreateSecretStoreInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstore/delete.go b/pkg/commands/secretstore/delete.go index 2982a0722..2cbe18c6d 100644 --- a/pkg/commands/secretstore/delete.go +++ b/pkg/commands/secretstore/delete.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a secret store") @@ -37,8 +35,7 @@ type DeleteCommand struct { cmd.Base cmd.JSONOutput - Input fastly.DeleteSecretStoreInput - manifest manifest.Data + Input fastly.DeleteSecretStoreInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstore/describe.go b/pkg/commands/secretstore/describe.go index fab9086c9..be8cb1b5c 100644 --- a/pkg/commands/secretstore/describe.go +++ b/pkg/commands/secretstore/describe.go @@ -3,21 +3,20 @@ package secretstore import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Retrieve a single secret store").Alias("get") @@ -36,8 +35,7 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - Input fastly.GetSecretStoreInput - manifest manifest.Data + Input fastly.GetSecretStoreInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstore/list.go b/pkg/commands/secretstore/list.go index 7411c3cb8..a89309fa4 100644 --- a/pkg/commands/secretstore/list.go +++ b/pkg/commands/secretstore/list.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List secret stores") @@ -37,8 +35,7 @@ type ListCommand struct { cmd.JSONOutput // NOTE: API returns 10 items even when --limit is set to smaller. - Input fastly.ListSecretStoresInput - manifest manifest.Data + Input fastly.ListSecretStoresInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstore/secretstore_test.go b/pkg/commands/secretstore/secretstore_test.go index 96be7878f..0308d2c56 100644 --- a/pkg/commands/secretstore/secretstore_test.go +++ b/pkg/commands/secretstore/secretstore_test.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/secretstore" fstfmt "github.com/fastly/cli/pkg/fmt" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -79,7 +80,7 @@ func TestCreateStoreCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.CreateSecretStoreFn var apiInvoked bool @@ -88,7 +89,7 @@ func TestCreateStoreCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -164,7 +165,7 @@ func TestDeleteStoreCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.DeleteSecretStoreFn var apiInvoked bool @@ -173,7 +174,7 @@ func TestDeleteStoreCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -254,7 +255,7 @@ func TestDescribeStoreCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.GetSecretStoreFn var apiInvoked bool @@ -263,7 +264,7 @@ func TestDescribeStoreCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -346,7 +347,7 @@ func TestListStoresCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstore.RootNameStore + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.ListSecretStoresFn var apiInvoked bool @@ -355,7 +356,7 @@ func TestListStoresCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/secretstoreentry/create.go b/pkg/commands/secretstoreentry/create.go index ce7bd7bf6..0d6a9559d 100644 --- a/pkg/commands/secretstoreentry/create.go +++ b/pkg/commands/secretstoreentry/create.go @@ -14,7 +14,6 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -43,12 +42,11 @@ func mustDecode(s string) []byte { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a new secret within specified store") @@ -83,7 +81,6 @@ type CreateCommand struct { cmd.JSONOutput Input fastly.CreateSecretInput - manifest manifest.Data recreate bool recreateAllow bool secretFile string diff --git a/pkg/commands/secretstoreentry/delete.go b/pkg/commands/secretstoreentry/delete.go index 05d8bcb8b..eda3b040d 100644 --- a/pkg/commands/secretstoreentry/delete.go +++ b/pkg/commands/secretstoreentry/delete.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a secret") @@ -38,8 +36,7 @@ type DeleteCommand struct { cmd.Base cmd.JSONOutput - Input fastly.DeleteSecretInput - manifest manifest.Data + Input fastly.DeleteSecretInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstoreentry/describe.go b/pkg/commands/secretstoreentry/describe.go index 654cf1fd3..45d7551ba 100644 --- a/pkg/commands/secretstoreentry/describe.go +++ b/pkg/commands/secretstoreentry/describe.go @@ -3,21 +3,20 @@ package secretstoreentry import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Retrieve a single secret").Alias("get") @@ -37,8 +36,7 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - Input fastly.GetSecretInput - manifest manifest.Data + Input fastly.GetSecretInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstoreentry/list.go b/pkg/commands/secretstoreentry/list.go index ad8fef6c5..2185d1f9a 100644 --- a/pkg/commands/secretstoreentry/list.go +++ b/pkg/commands/secretstoreentry/list.go @@ -8,17 +8,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List secrets within a specified store") @@ -39,8 +37,7 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - Input fastly.ListSecretsInput - manifest manifest.Data + Input fastly.ListSecretsInput } // Exec invokes the application logic for the command. diff --git a/pkg/commands/secretstoreentry/secretstoreentry_test.go b/pkg/commands/secretstoreentry/secretstoreentry_test.go index c97756bd6..2eea5bf9b 100644 --- a/pkg/commands/secretstoreentry/secretstoreentry_test.go +++ b/pkg/commands/secretstoreentry/secretstoreentry_test.go @@ -21,6 +21,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/commands/secretstoreentry" fstfmt "github.com/fastly/cli/pkg/fmt" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -229,11 +230,11 @@ func TestCreateSecretCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) if testcase.stdin != "" { var stdin bytes.Buffer stdin.WriteString(testcase.stdin) - opts.Stdin = &stdin + opts.Input = &stdin } f := testcase.api.CreateSecretFn @@ -248,7 +249,7 @@ func TestCreateSecretCommand(t *testing.T) { // hardcoded value. t.Setenv("FASTLY_USE_API_SIGNING_KEY", "1") - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -330,7 +331,7 @@ func TestDeleteSecretCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.DeleteSecretFn var apiInvoked bool @@ -339,7 +340,7 @@ func TestDeleteSecretCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -437,7 +438,7 @@ func TestDescribeSecretCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.GetSecretFn var apiInvoked bool @@ -446,7 +447,7 @@ func TestDescribeSecretCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -525,7 +526,7 @@ func TestListSecretsCommand(t *testing.T) { t.Run(testcase.args, func(t *testing.T) { var stdout bytes.Buffer args := testutil.Args(secretstoreentry.RootNameSecret + " " + testcase.args) - opts := testutil.NewRunOpts(args, &stdout) + opts := testutil.MockGlobalData(args, &stdout) f := testcase.api.ListSecretsFn var apiInvoked bool @@ -534,7 +535,7 @@ func TestListSecretsCommand(t *testing.T) { return f(i) } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/service/create.go b/pkg/commands/service/create.go index a3f2915d1..f1cd726e0 100644 --- a/pkg/commands/service/create.go +++ b/pkg/commands/service/create.go @@ -3,10 +3,11 @@ package service import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CreateCommand calls the Fastly API to create services. diff --git a/pkg/commands/service/delete.go b/pkg/commands/service/delete.go index e79d36ab3..7edf72bdc 100644 --- a/pkg/commands/service/delete.go +++ b/pkg/commands/service/delete.go @@ -4,30 +4,29 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete services. type DeleteCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeleteServiceInput force bool serviceName cmd.OptionalServiceNameID } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a Fastly service").Alias("remove") @@ -36,7 +35,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -50,7 +49,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D // Exec invokes the application logic for the command. func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } @@ -99,12 +98,12 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { // Ensure that VCL service users are unaffected by checking if the Service ID // was acquired via the fastly.toml manifest. if source == manifest.SourceFile { - if err := c.manifest.File.Read(manifest.Filename); err != nil { + if err := c.Globals.Manifest.File.Read(manifest.Filename); err != nil { c.Globals.ErrLog.Add(err) return fmt.Errorf("error reading fastly.toml: %w", err) } - c.manifest.File.ServiceID = "" - if err := c.manifest.File.Write(manifest.Filename); err != nil { + c.Globals.Manifest.File.ServiceID = "" + if err := c.Globals.Manifest.File.Write(manifest.Filename); err != nil { c.Globals.ErrLog.Add(err) return fmt.Errorf("error updating fastly.toml: %w", err) } diff --git a/pkg/commands/service/describe.go b/pkg/commands/service/describe.go index 7a6836b25..d4598ac8c 100644 --- a/pkg/commands/service/describe.go +++ b/pkg/commands/service/describe.go @@ -5,13 +5,14 @@ import ( "io" "strconv" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" "github.com/fastly/cli/pkg/time" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a service. @@ -19,18 +20,16 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.GetServiceInput serviceName cmd.OptionalServiceNameID } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show detailed information about a Fastly service").Alias("get") @@ -39,7 +38,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -57,7 +56,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/service/search.go b/pkg/commands/service/search.go index c02e1ec1d..eaa0391b0 100644 --- a/pkg/commands/service/search.go +++ b/pkg/commands/service/search.go @@ -3,12 +3,12 @@ package service import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // SearchCommand calls the Fastly API to describe a service. @@ -16,17 +16,15 @@ type SearchCommand struct { cmd.Base cmd.JSONOutput - Input fastly.SearchServiceInput - manifest manifest.Data + Input fastly.SearchServiceInput } // NewSearchCommand returns a usable command registered under the parent. -func NewSearchCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *SearchCommand { +func NewSearchCommand(parent cmd.Registerer, g *global.Data) *SearchCommand { c := SearchCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("search", "Search for a Fastly service by name") diff --git a/pkg/commands/service/service_test.go b/pkg/commands/service/service_test.go index 585d8a8ee..5bf506b5f 100644 --- a/pkg/commands/service/service_test.go +++ b/pkg/commands/service/service_test.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" @@ -61,8 +62,8 @@ func TestServiceCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -137,8 +138,8 @@ func TestServiceList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -197,8 +198,8 @@ func TestServiceDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -241,8 +242,8 @@ func TestServiceSearch(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -309,8 +310,8 @@ func TestServiceUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -400,8 +401,8 @@ func TestServiceDelete(t *testing.T) { }() var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - runOpts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + runOpts := testutil.MockGlobalData(testcase.args, &stdout) runOpts.APIClientFactory = mock.APIClient(testcase.api) return runOpts, nil } diff --git a/pkg/commands/service/update.go b/pkg/commands/service/update.go index e5721457b..26da2248b 100644 --- a/pkg/commands/service/update.go +++ b/pkg/commands/service/update.go @@ -4,11 +4,11 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to create services. @@ -17,18 +17,16 @@ type UpdateCommand struct { comment cmd.OptionalString input fastly.UpdateServiceInput - manifest manifest.Data name cmd.OptionalString serviceName cmd.OptionalServiceNameID } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a Fastly service") @@ -38,7 +36,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -52,7 +50,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U // Exec invokes the application logic for the command. func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/serviceauth/create.go b/pkg/commands/serviceauth/create.go index 94461b3c1..da62f1ae1 100644 --- a/pkg/commands/serviceauth/create.go +++ b/pkg/commands/serviceauth/create.go @@ -7,7 +7,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -19,18 +18,16 @@ var Permissions = []string{"full", "read_only", "purge_select", "purge_all"} type CreateCommand struct { cmd.Base input fastly.CreateServiceAuthorizationInput - manifest manifest.Data serviceName cmd.OptionalServiceNameID userID string } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create service authorization").Alias("add") @@ -44,7 +41,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -58,10 +55,10 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C // Exec invokes the application logic for the command. func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ - "Service ID": c.manifest.Flag.ServiceID, + "Service ID": c.Globals.Manifest.Flag.ServiceID, "Service Name": c.serviceName.Value, }) return err diff --git a/pkg/commands/serviceauth/delete.go b/pkg/commands/serviceauth/delete.go index 7526127c3..aa6d34224 100644 --- a/pkg/commands/serviceauth/delete.go +++ b/pkg/commands/serviceauth/delete.go @@ -3,27 +3,25 @@ package serviceauth import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete service authorizations. type DeleteCommand struct { cmd.Base - manifest manifest.Data - Input fastly.DeleteServiceAuthorizationInput + Input fastly.DeleteServiceAuthorizationInput } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete service authorization").Alias("remove") diff --git a/pkg/commands/serviceauth/describe.go b/pkg/commands/serviceauth/describe.go index dc141ca14..6d127c004 100644 --- a/pkg/commands/serviceauth/describe.go +++ b/pkg/commands/serviceauth/describe.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/time" - "github.com/fastly/go-fastly/v8/fastly" ) // DescribeCommand calls the Fastly API to describe a service authorization. @@ -17,17 +17,15 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data - Input fastly.GetServiceAuthorizationInput + Input fastly.GetServiceAuthorizationInput } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Show service authorization").Alias("get") diff --git a/pkg/commands/serviceauth/service_test.go b/pkg/commands/serviceauth/service_test.go index eb5cbcd94..88941b1e1 100644 --- a/pkg/commands/serviceauth/service_test.go +++ b/pkg/commands/serviceauth/service_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -41,8 +42,8 @@ func TestServiceAuthCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -110,8 +111,8 @@ func TestServiceAuthList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -171,8 +172,8 @@ func TestServiceAuthDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -215,8 +216,8 @@ func TestServiceAuthUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -254,8 +255,8 @@ func TestServiceAuthDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/serviceauth/update.go b/pkg/commands/serviceauth/update.go index 38b215072..c0b4079a5 100644 --- a/pkg/commands/serviceauth/update.go +++ b/pkg/commands/serviceauth/update.go @@ -3,28 +3,26 @@ package serviceauth import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // UpdateCommand calls the Fastly API to update service authorizations. type UpdateCommand struct { cmd.Base - input fastly.UpdateServiceAuthorizationInput - manifest manifest.Data + input fastly.UpdateServiceAuthorizationInput } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update service authorization") diff --git a/pkg/commands/serviceversion/activate.go b/pkg/commands/serviceversion/activate.go index ee2c69233..fd85144ae 100644 --- a/pkg/commands/serviceversion/activate.go +++ b/pkg/commands/serviceversion/activate.go @@ -3,18 +3,17 @@ package serviceversion import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ActivateCommand calls the Fastly API to activate a service version. type ActivateCommand struct { cmd.Base - manifest manifest.Data Input fastly.ActivateVersionInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,15 +21,14 @@ type ActivateCommand struct { } // NewActivateCommand returns a usable command registered under the parent. -func NewActivateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ActivateCommand { +func NewActivateCommand(parent cmd.Registerer, g *global.Data) *ActivateCommand { var c ActivateCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("activate", "Activate a Fastly service version") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -57,7 +55,7 @@ func (c *ActivateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/serviceversion/clone.go b/pkg/commands/serviceversion/clone.go index 10c1c5b1d..69f66e507 100644 --- a/pkg/commands/serviceversion/clone.go +++ b/pkg/commands/serviceversion/clone.go @@ -3,33 +3,31 @@ package serviceversion import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // CloneCommand calls the Fastly API to clone a service version. type CloneCommand struct { cmd.Base - manifest manifest.Data Input fastly.CloneVersionInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewCloneCommand returns a usable command registered under the parent. -func NewCloneCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CloneCommand { +func NewCloneCommand(parent cmd.Registerer, g *global.Data) *CloneCommand { var c CloneCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("clone", "Clone a Fastly service version") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -52,7 +50,7 @@ func (c *CloneCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/serviceversion/deactivate.go b/pkg/commands/serviceversion/deactivate.go index d569e5a9e..64056afc0 100644 --- a/pkg/commands/serviceversion/deactivate.go +++ b/pkg/commands/serviceversion/deactivate.go @@ -3,33 +3,31 @@ package serviceversion import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeactivateCommand calls the Fastly API to deactivate a service version. type DeactivateCommand struct { cmd.Base - manifest manifest.Data Input fastly.DeactivateVersionInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDeactivateCommand returns a usable command registered under the parent. -func NewDeactivateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeactivateCommand { +func NewDeactivateCommand(parent cmd.Registerer, g *global.Data) *DeactivateCommand { var c DeactivateCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("deactivate", "Deactivate a Fastly service version") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -52,7 +50,7 @@ func (c *DeactivateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/serviceversion/list.go b/pkg/commands/serviceversion/list.go index 17d6c775c..20aca08eb 100644 --- a/pkg/commands/serviceversion/list.go +++ b/pkg/commands/serviceversion/list.go @@ -4,13 +4,13 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" "github.com/fastly/cli/pkg/time" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list services. @@ -18,25 +18,23 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data Input fastly.ListVersionsInput serviceName cmd.OptionalServiceNameID } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List Fastly service versions") c.RegisterFlagBool(c.JSONFlag()) // --json c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -54,7 +52,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { return fsterr.ErrInvalidVerboseJSONCombo } - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/serviceversion/lock.go b/pkg/commands/serviceversion/lock.go index 64e6133a1..3aaf91db7 100644 --- a/pkg/commands/serviceversion/lock.go +++ b/pkg/commands/serviceversion/lock.go @@ -3,33 +3,31 @@ package serviceversion import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // LockCommand calls the Fastly API to lock a service version. type LockCommand struct { cmd.Base - manifest manifest.Data Input fastly.LockVersionInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewLockCommand returns a usable command registered under the parent. -func NewLockCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *LockCommand { +func NewLockCommand(parent cmd.Registerer, g *global.Data) *LockCommand { var c LockCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("lock", "Lock a Fastly service version") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -52,7 +50,7 @@ func (c *LockCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/serviceversion/serviceversion_test.go b/pkg/commands/serviceversion/serviceversion_test.go index 2d5976dcd..401a3970d 100644 --- a/pkg/commands/serviceversion/serviceversion_test.go +++ b/pkg/commands/serviceversion/serviceversion_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -50,8 +51,8 @@ func TestVersionClone(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -105,8 +106,8 @@ func TestVersionList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -156,8 +157,8 @@ func TestVersionUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -211,8 +212,8 @@ func TestVersionActivate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -264,8 +265,8 @@ func TestVersionDeactivate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } @@ -309,8 +310,8 @@ func TestVersionLock(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/serviceversion/update.go b/pkg/commands/serviceversion/update.go index b747f0c47..c1176f775 100644 --- a/pkg/commands/serviceversion/update.go +++ b/pkg/commands/serviceversion/update.go @@ -9,14 +9,12 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // UpdateCommand calls the Fastly API to update a service version. type UpdateCommand struct { cmd.Base - manifest manifest.Data input fastly.UpdateVersionInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -26,18 +24,17 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a Fastly service version") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -71,7 +68,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index ba133b762..b9451004d 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -19,9 +19,7 @@ import ( // It should be installed under the primary root command. type RootCommand struct { cmd.Base - authServer auth.Starter - openBrowser func(string) error - profile string + profile string // IMPORTANT: The following fields are public to the `profile` subcommands. @@ -38,10 +36,8 @@ type RootCommand struct { } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, g *global.Data, opener func(string) error, authServer auth.Starter) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand - c.authServer = authServer - c.openBrowser = opener c.Globals = g // FIXME: Unhide this command once SSO authentication goes GA. c.CmdClause = parent.Command("sso", "Single Sign-On authentication").Hidden() @@ -73,20 +69,12 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { accountEndpoint, _ := c.Globals.AccountEndpoint() apiEndpoint, _ := c.Globals.APIEndpoint() - verifier, err := auth.GenVerifier() - if err != nil { - return fsterr.RemediationError{ - Inner: fmt.Errorf("failed to generate a code verifier: %w", err), - Remediation: auth.Remediation, - } - } - c.authServer.SetAccountEndpoint(accountEndpoint) - c.authServer.SetAPIEndpoint(apiEndpoint) - c.authServer.SetVerifier(verifier) + c.Globals.AuthServer.SetAccountEndpoint(accountEndpoint) + c.Globals.AuthServer.SetAPIEndpoint(apiEndpoint) var serverErr error go func() { - err := c.authServer.Start() + err := c.Globals.AuthServer.Start() if err != nil { serverErr = err } @@ -97,7 +85,7 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { text.Info(out, "Starting a local server to handle the authentication flow.") - authorizationURL, err := auth.GenURL(accountEndpoint, apiEndpoint, verifier) + authorizationURL, err := c.Globals.AuthServer.AuthURL() if err != nil { return fsterr.RemediationError{ Inner: fmt.Errorf("failed to generate an authorization URL: %w", err), @@ -108,12 +96,12 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { text.Break(out) text.Description(out, "We're opening the following URL in your default web browser so you may authenticate with Fastly", authorizationURL) - err = c.openBrowser(authorizationURL) + err = c.Globals.Opener(authorizationURL) if err != nil { return fmt.Errorf("failed to open your default browser: %w", err) } - ar := <-c.authServer.GetResult() + ar := <-c.Globals.AuthServer.GetResult() if ar.Err != nil || ar.SessionToken == "" { err := ar.Err if ar.Err == nil { diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index 57f6049ef..03d106859 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -13,6 +13,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/config" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -226,14 +227,14 @@ func TestSSO(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.Args, &stdout) + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) if testcase.HTTPClient != nil { opts.HTTPClient = testcase.HTTPClient } if testcase.ConfigFile != nil { - opts.ConfigFile = *testcase.ConfigFile + opts.Config = *testcase.ConfigFile } if testcase.Opener != nil { opts.Opener = testcase.Opener @@ -254,7 +255,7 @@ func TestSSO(t *testing.T) { // To handle multiple prompt input from the user we need to do some // coordination around io pipes to mimic the required user behaviour. stdin, prompt := io.Pipe() - opts.Stdin = stdin + opts.Input = stdin // Wait for user input and write it to the prompt inputc := make(chan string) @@ -269,7 +270,7 @@ func TestSSO(t *testing.T) { // Call `app.Run()` and wait for response go func() { - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -296,8 +297,8 @@ func TestSSO(t *testing.T) { if len(testcase.Stdin) > 0 { stdin = testcase.Stdin[0] } - opts.Stdin = strings.NewReader(stdin) - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + opts.Input = strings.NewReader(stdin) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(testcase.Args, nil) @@ -308,7 +309,7 @@ func TestSSO(t *testing.T) { if len(testcase.Args) > 1 { profileName = testcase.Args[1] // use the `profile` command argument } - userProfile := opts.ConfigFile.Profiles[profileName] + userProfile := opts.Config.Profiles[profileName] if userProfile.Token != testcase.ExpectedConfigProfile.Token { t.Errorf("want token: %s, got token: %s", testcase.ExpectedConfigProfile.Token, userProfile.Token) } diff --git a/pkg/commands/stats/historical.go b/pkg/commands/stats/historical.go index a05e46b37..4a2a3cb99 100644 --- a/pkg/commands/stats/historical.go +++ b/pkg/commands/stats/historical.go @@ -9,7 +9,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) const statusSuccess = "success" @@ -17,7 +16,6 @@ const statusSuccess = "success" // HistoricalCommand exposes the Historical Stats API. type HistoricalCommand struct { cmd.Base - manifest manifest.Data Input fastly.GetStatsInput formatFlag string @@ -25,16 +23,15 @@ type HistoricalCommand struct { } // NewHistoricalCommand is the "stats historical" subcommand. -func NewHistoricalCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *HistoricalCommand { +func NewHistoricalCommand(parent cmd.Registerer, g *global.Data) *HistoricalCommand { var c HistoricalCommand c.Globals = g - c.manifest = m c.CmdClause = parent.Command("historical", "View historical stats for a Fastly service") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -56,7 +53,7 @@ func NewHistoricalCommand(parent cmd.Registerer, g *global.Data, m manifest.Data // Exec implements the command interface. func (c *HistoricalCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/stats/historical_test.go b/pkg/commands/stats/historical_test.go index ba5d65480..3605c65bb 100644 --- a/pkg/commands/stats/historical_test.go +++ b/pkg/commands/stats/historical_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -42,8 +43,8 @@ func TestHistorical(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/stats/realtime.go b/pkg/commands/stats/realtime.go index d9c771b9b..d8df25c60 100644 --- a/pkg/commands/stats/realtime.go +++ b/pkg/commands/stats/realtime.go @@ -5,34 +5,32 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/api" "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // RealtimeCommand exposes the Realtime Metrics API. type RealtimeCommand struct { cmd.Base - manifest manifest.Data formatFlag string serviceName cmd.OptionalServiceNameID } // NewRealtimeCommand is the "stats realtime" subcommand. -func NewRealtimeCommand(parent cmd.Registerer, g *global.Data, data manifest.Data) *RealtimeCommand { +func NewRealtimeCommand(parent cmd.Registerer, g *global.Data) *RealtimeCommand { var c RealtimeCommand c.Globals = g - c.manifest = data c.CmdClause = parent.Command("realtime", "View realtime stats for a Fastly service") c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -49,7 +47,7 @@ func NewRealtimeCommand(parent cmd.Registerer, g *global.Data, data manifest.Dat // Exec implements the command interface. func (c *RealtimeCommand) Exec(_ io.Reader, out io.Writer) error { - serviceID, source, flag, err := cmd.ServiceID(c.serviceName, c.manifest, c.Globals.APIClient, c.Globals.ErrLog) + serviceID, source, flag, err := cmd.ServiceID(c.serviceName, *c.Globals.Manifest, c.Globals.APIClient, c.Globals.ErrLog) if err != nil { return err } diff --git a/pkg/commands/stats/regions_test.go b/pkg/commands/stats/regions_test.go index 66fadb5ff..d01b09134 100644 --- a/pkg/commands/stats/regions_test.go +++ b/pkg/commands/stats/regions_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -37,8 +38,8 @@ func TestRegions(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(strings.Join(testcase.args, " "), func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.api) return opts, nil } diff --git a/pkg/commands/tls/config/config_test.go b/pkg/commands/tls/config/config_test.go index f522a8ee5..e455ad9ae 100644 --- a/pkg/commands/tls/config/config_test.go +++ b/pkg/commands/tls/config/config_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -70,8 +71,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -130,8 +131,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -183,8 +184,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/config/describe.go b/pkg/commands/tls/config/describe.go index 55c74fbbb..e4bae4ece 100644 --- a/pkg/commands/tls/config/describe.go +++ b/pkg/commands/tls/config/describe.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) const include = "dns_records" // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Show a TLS configuration").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS configuration").Required().StringVar(&c.id) @@ -35,9 +34,8 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - include string - manifest manifest.Data + id string + include string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/config/list.go b/pkg/commands/tls/config/list.go index 8bd2fde28..92e18b228 100644 --- a/pkg/commands/tls/config/list.go +++ b/pkg/commands/tls/config/list.go @@ -5,20 +5,19 @@ import ( "io" "strings" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all TLS configurations") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-bulk", "Optionally filter by the bulk attribute").Action(c.filterBulk.Set).BoolVar(&c.filterBulk.Value) @@ -37,7 +36,6 @@ type ListCommand struct { filterBulk cmd.OptionalBool include string - manifest manifest.Data pageNumber int pageSize int } diff --git a/pkg/commands/tls/config/update.go b/pkg/commands/tls/config/update.go index a3e1683e3..e5c1c3b85 100644 --- a/pkg/commands/tls/config/update.go +++ b/pkg/commands/tls/config/update.go @@ -3,19 +3,18 @@ package config import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command("update", "Update a TLS configuration") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS configuration").Required().StringVar(&c.id) @@ -27,9 +26,8 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U type UpdateCommand struct { cmd.Base - id string - manifest manifest.Data - name string + id string + name string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/activation/activation_test.go b/pkg/commands/tls/custom/activation/activation_test.go index c370e35d0..277dd6a1f 100644 --- a/pkg/commands/tls/custom/activation/activation_test.go +++ b/pkg/commands/tls/custom/activation/activation_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -65,8 +66,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -111,8 +112,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -161,8 +162,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -208,8 +209,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -264,8 +265,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/custom/activation/create.go b/pkg/commands/tls/custom/activation/create.go index 878bdb6f1..80145488d 100644 --- a/pkg/commands/tls/custom/activation/create.go +++ b/pkg/commands/tls/custom/activation/create.go @@ -3,19 +3,18 @@ package activation import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { var c CreateCommand c.CmdClause = parent.Command("enable", "Enable TLS for a particular TLS domain and certificate combination").Alias("add") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("cert-id", "Alphanumeric string identifying a TLS certificate").Required().StringVar(&c.certID) @@ -28,9 +27,8 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C type CreateCommand struct { cmd.Base - certID string - id string - manifest manifest.Data + certID string + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/activation/delete.go b/pkg/commands/tls/custom/activation/delete.go index 8fe8f2360..5deb1ee3f 100644 --- a/pkg/commands/tls/custom/activation/delete.go +++ b/pkg/commands/tls/custom/activation/delete.go @@ -3,19 +3,18 @@ package activation import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("disable", "Disable TLS on the domain associated with this TLS activation").Alias("remove") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS activation").Required().StringVar(&c.id) @@ -27,8 +26,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/activation/describe.go b/pkg/commands/tls/custom/activation/describe.go index 966bbd952..bfd82d7bb 100644 --- a/pkg/commands/tls/custom/activation/describe.go +++ b/pkg/commands/tls/custom/activation/describe.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) var include = []string{"tls_certificate", "tls_configuration", "tls_domain"} // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Show a TLS configuration").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS activation").Required().StringVar(&c.id) @@ -35,9 +34,8 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - include string - manifest manifest.Data + id string + include string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/activation/list.go b/pkg/commands/tls/custom/activation/list.go index 831a6343b..9525e76fa 100644 --- a/pkg/commands/tls/custom/activation/list.go +++ b/pkg/commands/tls/custom/activation/list.go @@ -4,20 +4,19 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all TLS activations") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-cert", "Limit the returned activations to a specific certificate").StringVar(&c.filterTLSCertID) @@ -40,7 +39,6 @@ type ListCommand struct { filterTLSConfigID string filterTLSDomainID string include string - manifest manifest.Data pageNumber int pageSize int } diff --git a/pkg/commands/tls/custom/activation/update.go b/pkg/commands/tls/custom/activation/update.go index 61ac31b4f..f8345ebf1 100644 --- a/pkg/commands/tls/custom/activation/update.go +++ b/pkg/commands/tls/custom/activation/update.go @@ -3,19 +3,18 @@ package activation import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command("update", "Update the certificate used to terminate TLS traffic for the domain associated with this TLS activation") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("cert-id", "Alphanumeric string identifying a TLS certificate").Required().StringVar(&c.certID) @@ -27,9 +26,8 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U type UpdateCommand struct { cmd.Base - certID string - id string - manifest manifest.Data + certID string + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/certificate/certificate_test.go b/pkg/commands/tls/custom/certificate/certificate_test.go index 88578036d..1b62f25e0 100644 --- a/pkg/commands/tls/custom/certificate/certificate_test.go +++ b/pkg/commands/tls/custom/certificate/certificate_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -57,8 +58,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -103,8 +104,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -160,8 +161,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -214,8 +215,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -280,8 +281,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/custom/certificate/create.go b/pkg/commands/tls/custom/certificate/create.go index e240fdaaa..04aab4ff3 100644 --- a/pkg/commands/tls/custom/certificate/create.go +++ b/pkg/commands/tls/custom/certificate/create.go @@ -3,19 +3,18 @@ package certificate import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { var c CreateCommand c.CmdClause = parent.Command("create", "Create a TLS certificate").Alias("add") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("cert-blob", "The PEM-formatted certificate blob").Required().StringVar(&c.certBlob) @@ -33,7 +32,6 @@ type CreateCommand struct { certBlob string id string - manifest manifest.Data name string } diff --git a/pkg/commands/tls/custom/certificate/delete.go b/pkg/commands/tls/custom/certificate/delete.go index cf2d6b912..42d4c379b 100644 --- a/pkg/commands/tls/custom/certificate/delete.go +++ b/pkg/commands/tls/custom/certificate/delete.go @@ -3,19 +3,18 @@ package certificate import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("delete", "Destroy a TLS certificate. TLS certificates already enabled for a domain cannot be destroyed").Alias("remove") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS certificate").Required().StringVar(&c.id) @@ -27,8 +26,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/certificate/describe.go b/pkg/commands/tls/custom/certificate/describe.go index 1e4644a42..30f4e5d37 100644 --- a/pkg/commands/tls/custom/certificate/describe.go +++ b/pkg/commands/tls/custom/certificate/describe.go @@ -4,19 +4,18 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Show a TLS certificate").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS certificate").Required().StringVar(&c.id) @@ -32,8 +31,7 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/certificate/list.go b/pkg/commands/tls/custom/certificate/list.go index 1055d051b..a9aa9e35f 100644 --- a/pkg/commands/tls/custom/certificate/list.go +++ b/pkg/commands/tls/custom/certificate/list.go @@ -4,22 +4,21 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) const emptyString = "" // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all TLS certificates") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-not-after", "Limit the returned certificates to those that expire prior to the specified date in UTC").StringVar(&c.filterNotAfter) @@ -41,7 +40,6 @@ type ListCommand struct { filterNotAfter string filterTLSDomainID string include string - manifest manifest.Data pageNumber int pageSize int sort string diff --git a/pkg/commands/tls/custom/certificate/update.go b/pkg/commands/tls/custom/certificate/update.go index 3898de8be..42b45ca6b 100644 --- a/pkg/commands/tls/custom/certificate/update.go +++ b/pkg/commands/tls/custom/certificate/update.go @@ -3,19 +3,18 @@ package certificate import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command("update", "Replace a TLS certificate with a newly reissued TLS certificate, or update a TLS certificate's name") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("cert-blob", "The PEM-formatted certificate blob").Required().StringVar(&c.certBlob) @@ -32,7 +31,6 @@ type UpdateCommand struct { certBlob string id string - manifest manifest.Data name string } diff --git a/pkg/commands/tls/custom/domain/domain_test.go b/pkg/commands/tls/custom/domain/domain_test.go index 280442192..029bee357 100644 --- a/pkg/commands/tls/custom/domain/domain_test.go +++ b/pkg/commands/tls/custom/domain/domain_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -52,8 +53,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/custom/domain/list.go b/pkg/commands/tls/custom/domain/list.go index 9c70f39a8..26572346a 100644 --- a/pkg/commands/tls/custom/domain/list.go +++ b/pkg/commands/tls/custom/domain/list.go @@ -4,22 +4,21 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) const emptyString = "" // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all TLS domains") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-cert", "Limit the returned domains to those listed in the given TLS certificate's SAN list").StringVar(&c.filterTLSCertsID) @@ -43,7 +42,6 @@ type ListCommand struct { filterTLSCertsID string filterTLSSubsID string include string - manifest manifest.Data pageNumber int pageSize int sort string diff --git a/pkg/commands/tls/custom/privatekey/create.go b/pkg/commands/tls/custom/privatekey/create.go index f2568903d..c5172aee2 100644 --- a/pkg/commands/tls/custom/privatekey/create.go +++ b/pkg/commands/tls/custom/privatekey/create.go @@ -3,19 +3,18 @@ package privatekey import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { var c CreateCommand c.CmdClause = parent.Command("create", "Create a TLS certificate").Alias("add") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("key", "The contents of the private key. Must be a PEM-formatted key").Required().StringVar(&c.key) @@ -28,9 +27,8 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C type CreateCommand struct { cmd.Base - key string - manifest manifest.Data - name string + key string + name string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/privatekey/delete.go b/pkg/commands/tls/custom/privatekey/delete.go index 58aa8cfe3..8e80d2d7e 100644 --- a/pkg/commands/tls/custom/privatekey/delete.go +++ b/pkg/commands/tls/custom/privatekey/delete.go @@ -3,19 +3,18 @@ package privatekey import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("delete", "Destroy a TLS private key. Only private keys not already matched to any certificates can be deleted").Alias("remove") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a private Key").Required().StringVar(&c.id) @@ -27,8 +26,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/privatekey/describe.go b/pkg/commands/tls/custom/privatekey/describe.go index 1ae37cc72..e6af1f1e1 100644 --- a/pkg/commands/tls/custom/privatekey/describe.go +++ b/pkg/commands/tls/custom/privatekey/describe.go @@ -4,19 +4,18 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Show a TLS private key").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a private Key").Required().StringVar(&c.id) @@ -32,8 +31,7 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/custom/privatekey/list.go b/pkg/commands/tls/custom/privatekey/list.go index 6e4c7da31..2bc2882fa 100644 --- a/pkg/commands/tls/custom/privatekey/list.go +++ b/pkg/commands/tls/custom/privatekey/list.go @@ -4,20 +4,19 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all TLS private keys") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-in-use", "Limit the returned keys to those without any matching TLS certificates").HintOptions("false").EnumVar(&c.filterInUse, "false") @@ -34,7 +33,6 @@ type ListCommand struct { cmd.JSONOutput filterInUse string - manifest manifest.Data pageNumber int pageSize int } diff --git a/pkg/commands/tls/custom/privatekey/privatekey_test.go b/pkg/commands/tls/custom/privatekey/privatekey_test.go index 84835b84d..16891f8c0 100644 --- a/pkg/commands/tls/custom/privatekey/privatekey_test.go +++ b/pkg/commands/tls/custom/privatekey/privatekey_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -63,8 +64,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -109,8 +110,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -163,8 +164,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -214,8 +215,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/platform/create.go b/pkg/commands/tls/platform/create.go index ae987c5bc..e13e449ae 100644 --- a/pkg/commands/tls/platform/create.go +++ b/pkg/commands/tls/platform/create.go @@ -3,19 +3,18 @@ package platform import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { var c CreateCommand c.CmdClause = parent.Command("upload", "Upload a new certificate") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("cert-blob", "The PEM-formatted certificate blob").Required().StringVar(&c.certBlob) @@ -36,7 +35,6 @@ type CreateCommand struct { certBlob string config []string intermediatesBlob string - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/platform/delete.go b/pkg/commands/tls/platform/delete.go index 512a2e994..a7be1ff92 100644 --- a/pkg/commands/tls/platform/delete.go +++ b/pkg/commands/tls/platform/delete.go @@ -3,19 +3,18 @@ package platform import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("delete", "Destroy a certificate. This disables TLS for all domains listed as SAN entries").Alias("remove") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS bulk certificate").Required().StringVar(&c.id) @@ -27,8 +26,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/platform/describe.go b/pkg/commands/tls/platform/describe.go index 9c26bf759..cf91ca9da 100644 --- a/pkg/commands/tls/platform/describe.go +++ b/pkg/commands/tls/platform/describe.go @@ -4,19 +4,18 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Retrieve a single certificate").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS bulk certificate").Required().StringVar(&c.id) @@ -32,8 +31,7 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/platform/list.go b/pkg/commands/tls/platform/list.go index 0655f05db..91ac0cefb 100644 --- a/pkg/commands/tls/platform/list.go +++ b/pkg/commands/tls/platform/list.go @@ -4,20 +4,19 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all certificates") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-domain", "Optionally filter by the bulk attribute").StringVar(&c.filterTLSDomainID) @@ -35,7 +34,6 @@ type ListCommand struct { cmd.JSONOutput filterTLSDomainID string - manifest manifest.Data pageNumber int pageSize int sort string diff --git a/pkg/commands/tls/platform/platform_test.go b/pkg/commands/tls/platform/platform_test.go index 72219eed5..21bfc060c 100644 --- a/pkg/commands/tls/platform/platform_test.go +++ b/pkg/commands/tls/platform/platform_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -61,8 +62,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -107,8 +108,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -159,8 +160,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -208,8 +209,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -266,8 +267,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/platform/update.go b/pkg/commands/tls/platform/update.go index 2548eb75d..ce9549313 100644 --- a/pkg/commands/tls/platform/update.go +++ b/pkg/commands/tls/platform/update.go @@ -3,23 +3,20 @@ package platform import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand( - parent cmd.Registerer, g *global.Data, m manifest.Data, -) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command( "update", "Replace a certificate with a newly reissued certificate", ) c.Globals = g - c.manifest = m // Required. @@ -52,7 +49,6 @@ type UpdateCommand struct { certBlob string id string intermediatesBlob string - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/subscription/create.go b/pkg/commands/tls/subscription/create.go index 035be6b4a..b0a6f3ae9 100644 --- a/pkg/commands/tls/subscription/create.go +++ b/pkg/commands/tls/subscription/create.go @@ -3,11 +3,11 @@ package subscription import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) const emptyString = "" @@ -15,11 +15,10 @@ const emptyString = "" var certAuth = []string{"lets-encrypt", "globalsign"} // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { var c CreateCommand c.CmdClause = parent.Command("create", "Create a new TLS subscription").Alias("add") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("domain", "Domain(s) to add to the TLS certificates generated for the subscription (set flag once per domain)").Required().StringsVar(&c.domains) @@ -40,7 +39,6 @@ type CreateCommand struct { commonName string config string domains []string - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/subscription/delete.go b/pkg/commands/tls/subscription/delete.go index a2f5494bd..f84cd9e70 100644 --- a/pkg/commands/tls/subscription/delete.go +++ b/pkg/commands/tls/subscription/delete.go @@ -3,19 +3,18 @@ package subscription import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("delete", "Destroy a TLS subscription. A subscription cannot be destroyed if there are domains in the TLS enabled state").Alias("remove") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS subscription").Required().StringVar(&c.id) @@ -30,9 +29,8 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D type DeleteCommand struct { cmd.Base - force cmd.OptionalBool - id string - manifest manifest.Data + force cmd.OptionalBool + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/subscription/describe.go b/pkg/commands/tls/subscription/describe.go index a10f6b625..229ad55f0 100644 --- a/pkg/commands/tls/subscription/describe.go +++ b/pkg/commands/tls/subscription/describe.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) var include = []string{"tls_authorizations", "tls_authorizations.globalsign_email_challenge"} // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Show a TLS subscription").Alias("get") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS subscription").Required().StringVar(&c.id) @@ -35,9 +34,8 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - id string - include string - manifest manifest.Data + id string + include string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/tls/subscription/list.go b/pkg/commands/tls/subscription/list.go index dcd4d300a..9589423c6 100644 --- a/pkg/commands/tls/subscription/list.go +++ b/pkg/commands/tls/subscription/list.go @@ -4,22 +4,21 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) var states = []string{"pending", "processing", "issued", "renewing"} // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all TLS subscriptions") c.Globals = g - c.manifest = m // Optional. c.CmdClause.Flag("filter-active", "Limit the returned subscriptions to those that have currently active orders").BoolVar(&c.filterHasActiveOrder) @@ -43,7 +42,6 @@ type ListCommand struct { filterState string filterTLSDomainID string include string - manifest manifest.Data pageNumber int pageSize int sort string diff --git a/pkg/commands/tls/subscription/subscription_test.go b/pkg/commands/tls/subscription/subscription_test.go index 8ae798002..f600f46f3 100644 --- a/pkg/commands/tls/subscription/subscription_test.go +++ b/pkg/commands/tls/subscription/subscription_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -61,8 +62,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -107,8 +108,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -160,8 +161,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -210,8 +211,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -262,8 +263,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/tls/subscription/update.go b/pkg/commands/tls/subscription/update.go index 155dcda29..e983ee55e 100644 --- a/pkg/commands/tls/subscription/update.go +++ b/pkg/commands/tls/subscription/update.go @@ -3,19 +3,18 @@ package subscription import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command("update", "Change the TLS domains or common name associated with this subscription, or update the TLS configuration for this set of domains") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("id", "Alphanumeric string identifying a TLS subscription").Required().StringVar(&c.id) @@ -38,7 +37,6 @@ type UpdateCommand struct { domains []string force cmd.OptionalBool id string - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/update/root.go b/pkg/commands/update/root.go index 03a76f404..3a9ed90d5 100644 --- a/pkg/commands/update/root.go +++ b/pkg/commands/update/root.go @@ -10,7 +10,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/filesystem" - "github.com/fastly/cli/pkg/github" "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/revision" "github.com/fastly/cli/pkg/text" @@ -20,17 +19,13 @@ import ( // It should be installed under the primary root command. type RootCommand struct { cmd.Base - av github.AssetVersioner - configFilePath string } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, configFilePath string, av github.AssetVersioner, g *global.Data) *RootCommand { +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand c.Globals = g c.CmdClause = parent.Command("update", "Update the CLI to the latest version") - c.av = av - c.configFilePath = configFilePath return &c } @@ -47,7 +42,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { ) err = spinner.Process("Updating versioning information", func(_ *text.SpinnerWrapper) error { - current, latest, shouldUpdate = Check(revision.AppVersion, c.av) + current, latest, shouldUpdate = Check(revision.AppVersion, c.Globals.Versioners.CLI) return nil }) if err != nil { @@ -66,7 +61,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { var downloadedBin string err = spinner.Process("Fetching latest release", func(_ *text.SpinnerWrapper) error { - downloadedBin, err = c.av.DownloadLatest() + downloadedBin, err = c.Globals.Versioners.CLI.DownloadLatest() if err != nil { c.Globals.ErrLog.AddWithContext(err, map[string]any{ "Current CLI version": current, diff --git a/pkg/commands/user/create.go b/pkg/commands/user/create.go index d106438f7..2d65b001d 100644 --- a/pkg/commands/user/create.go +++ b/pkg/commands/user/create.go @@ -7,16 +7,14 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { var c CreateCommand c.CmdClause = parent.Command("create", "Create a user of the Fastly API and web interface").Alias("add") c.Globals = g - c.manifest = m // Required. c.CmdClause.Flag("login", "The login associated with the user (typically, an email address)").Action(c.login.Set).StringVar(&c.login.Value) @@ -31,7 +29,6 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C // CreateCommand calls the Fastly API to create an appropriate resource. type CreateCommand struct { cmd.Base - manifest manifest.Data login cmd.OptionalString name cmd.OptionalString diff --git a/pkg/commands/user/delete.go b/pkg/commands/user/delete.go index d59e988c2..1cfbb785b 100644 --- a/pkg/commands/user/delete.go +++ b/pkg/commands/user/delete.go @@ -7,16 +7,14 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, globals *global.Data) *DeleteCommand { var c DeleteCommand c.CmdClause = parent.Command("delete", "Delete a user of the Fastly API and web interface").Alias("remove") c.Globals = globals - c.manifest = m c.CmdClause.Flag("id", "Alphanumeric string identifying the user").Required().StringVar(&c.id) return &c } @@ -25,8 +23,7 @@ func NewDeleteCommand(parent cmd.Registerer, globals *global.Data, m manifest.Da type DeleteCommand struct { cmd.Base - id string - manifest manifest.Data + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/user/describe.go b/pkg/commands/user/describe.go index 6ba6085f7..cfe4b9d07 100644 --- a/pkg/commands/user/describe.go +++ b/pkg/commands/user/describe.go @@ -9,15 +9,13 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Get a specific user of the Fastly API and web interface").Alias("get") c.Globals = g - c.manifest = m c.CmdClause.Flag("current", "Get the logged in user").BoolVar(&c.current) c.CmdClause.Flag("id", "Alphanumeric string identifying the user").StringVar(&c.id) c.RegisterFlagBool(c.JSONFlag()) // --json @@ -29,9 +27,8 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - current bool - id string - manifest manifest.Data + current bool + id string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/user/list.go b/pkg/commands/user/list.go index 6336517b9..402ddc198 100644 --- a/pkg/commands/user/list.go +++ b/pkg/commands/user/list.go @@ -9,16 +9,14 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List all users from a specified customer id") c.Globals = g - c.manifest = m c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagCustomerIDName, Description: cmd.FlagCustomerIDDesc, @@ -35,7 +33,6 @@ type ListCommand struct { cmd.JSONOutput customerID cmd.OptionalCustomerID - manifest manifest.Data } // Exec invokes the application logic for the command. diff --git a/pkg/commands/user/update.go b/pkg/commands/user/update.go index f9697be94..068ae92a8 100644 --- a/pkg/commands/user/update.go +++ b/pkg/commands/user/update.go @@ -8,16 +8,14 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command("update", "Update a user of the Fastly API and web interface") c.Globals = g - c.manifest = m c.CmdClause.Flag("id", "Alphanumeric string identifying the user").StringVar(&c.id) c.CmdClause.Flag("login", "The login associated with the user (typically, an email address)").StringVar(&c.login) c.CmdClause.Flag("name", "The real life name of the user").StringVar(&c.name) @@ -31,12 +29,11 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U type UpdateCommand struct { cmd.Base - id string - login string - manifest manifest.Data - name string - reset bool - role string + id string + login string + name string + reset bool + role string } // Exec invokes the application logic for the command. diff --git a/pkg/commands/user/user_test.go b/pkg/commands/user/user_test.go index 0b3efcf36..dff1f93f1 100644 --- a/pkg/commands/user/user_test.go +++ b/pkg/commands/user/user_test.go @@ -9,6 +9,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -45,8 +46,8 @@ func TestCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -91,8 +92,8 @@ func TestDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -153,8 +154,8 @@ func TestDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -205,8 +206,8 @@ func TestList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -290,8 +291,8 @@ func TestUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/vcl/condition/condition_test.go b/pkg/commands/vcl/condition/condition_test.go index 0f7d34098..20ce9bf3f 100644 --- a/pkg/commands/vcl/condition/condition_test.go +++ b/pkg/commands/vcl/condition/condition_test.go @@ -10,6 +10,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -44,8 +45,8 @@ func TestConditionCreate(t *testing.T) { for _, testcase := range scenarios { t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -86,8 +87,8 @@ func TestConditionDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -137,8 +138,8 @@ func TestConditionUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -177,8 +178,8 @@ func TestConditionDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -245,8 +246,8 @@ func TestConditionList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/vcl/condition/create.go b/pkg/commands/vcl/condition/create.go index 16423d8ce..db9fa4c45 100644 --- a/pkg/commands/vcl/condition/create.go +++ b/pkg/commands/vcl/condition/create.go @@ -8,7 +8,6 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) @@ -19,7 +18,6 @@ var ConditionTypes = []string{"REQUEST", "CACHE", "RESPONSE", "PREFETCH"} // CreateCommand calls the Fastly API to create an appropriate resource. type CreateCommand struct { cmd.Base - manifest manifest.Data // Required. serviceVersion cmd.OptionalServiceVersion @@ -34,12 +32,11 @@ type CreateCommand struct { } // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a condition on a Fastly service version").Alias("add") @@ -63,7 +60,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -81,7 +78,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/condition/delete.go b/pkg/commands/vcl/condition/delete.go index 1cd433f3b..9158a6439 100644 --- a/pkg/commands/vcl/condition/delete.go +++ b/pkg/commands/vcl/condition/delete.go @@ -3,18 +3,17 @@ package condition import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // DeleteCommand calls the Fastly API to delete an appropriate resource. type DeleteCommand struct { cmd.Base - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -22,12 +21,11 @@ type DeleteCommand struct { } // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a condition on a Fastly service version").Alias("remove") @@ -48,7 +46,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -66,7 +64,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/condition/describe.go b/pkg/commands/vcl/condition/describe.go index 55e3e2e89..8112be201 100644 --- a/pkg/commands/vcl/condition/describe.go +++ b/pkg/commands/vcl/condition/describe.go @@ -9,25 +9,22 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) // DescribeCommand calls the Fastly API to describe an appropriate resource. type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { var c DescribeCommand c.CmdClause = parent.Command("describe", "Show detailed information about a condition on a Fastly service version").Alias("get") c.Globals = g - c.manifest = m // Required flags c.CmdClause.Flag("name", "Name of condition").Short('n').Required().StringVar(&c.name) @@ -44,7 +41,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -66,7 +63,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/condition/list.go b/pkg/commands/vcl/condition/list.go index affc20eb2..98774aa59 100644 --- a/pkg/commands/vcl/condition/list.go +++ b/pkg/commands/vcl/condition/list.go @@ -4,12 +4,12 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // ListCommand calls the Fastly API to list appropriate resources. @@ -17,17 +17,15 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, data manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { var c ListCommand c.CmdClause = parent.Command("list", "List condition on a Fastly service version") c.Globals = g - c.manifest = data // Required flags c.RegisterFlag(cmd.StringFlagOpts{ @@ -41,7 +39,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, data manifest.Data) * c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -63,7 +61,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/condition/update.go b/pkg/commands/vcl/condition/update.go index 895fd8788..514ade8aa 100644 --- a/pkg/commands/vcl/condition/update.go +++ b/pkg/commands/vcl/condition/update.go @@ -9,14 +9,12 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // UpdateCommand calls the Fastly API to update an appropriate resource. type UpdateCommand struct { cmd.Base - manifest manifest.Data input fastly.UpdateConditionInput serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -30,11 +28,10 @@ type UpdateCommand struct { } // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, globals *global.Data, data manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { var c UpdateCommand c.CmdClause = parent.Command("update", "Update a condition on a Fastly service version") - c.Globals = globals - c.manifest = data + c.Globals = g // Required flags c.CmdClause.Flag("name", "Domain name").Short('n').Required().StringVar(&c.input.Name) @@ -59,7 +56,7 @@ func NewUpdateCommand(parent cmd.Registerer, globals *global.Data, data manifest c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -77,7 +74,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/custom/create.go b/pkg/commands/vcl/custom/create.go index b1545f6ed..1dfc30187 100644 --- a/pkg/commands/vcl/custom/create.go +++ b/pkg/commands/vcl/custom/create.go @@ -3,21 +3,20 @@ package custom import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Upload a VCL for a particular service and version").Alias("add") @@ -40,7 +39,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -60,7 +59,6 @@ type CreateCommand struct { autoClone cmd.OptionalAutoClone content cmd.OptionalString main cmd.OptionalBool - manifest manifest.Data name cmd.OptionalString serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -71,7 +69,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/custom/custom_test.go b/pkg/commands/vcl/custom/custom_test.go index 7a14df390..7c4ffd3a9 100644 --- a/pkg/commands/vcl/custom/custom_test.go +++ b/pkg/commands/vcl/custom/custom_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -159,8 +160,8 @@ func TestVCLCustomCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -238,8 +239,8 @@ func TestVCLCustomDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -303,8 +304,8 @@ func TestVCLCustomDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -372,8 +373,8 @@ func TestVCLCustomList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -486,8 +487,8 @@ func TestVCLCustomUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/vcl/custom/delete.go b/pkg/commands/vcl/custom/delete.go index c193306e4..4c546af25 100644 --- a/pkg/commands/vcl/custom/delete.go +++ b/pkg/commands/vcl/custom/delete.go @@ -3,21 +3,20 @@ package custom import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete the uploaded VCL for a particular service and version").Alias("remove") @@ -38,7 +37,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -56,7 +55,6 @@ type DeleteCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -67,7 +65,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/custom/describe.go b/pkg/commands/vcl/custom/describe.go index 2a2327ff1..c7457a537 100644 --- a/pkg/commands/vcl/custom/describe.go +++ b/pkg/commands/vcl/custom/describe.go @@ -4,20 +4,19 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Get the uploaded VCL for a particular service and version").Alias("get") @@ -35,7 +34,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type DescribeCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -68,7 +66,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/custom/list.go b/pkg/commands/vcl/custom/list.go index 4faa3d688..766635229 100644 --- a/pkg/commands/vcl/custom/list.go +++ b/pkg/commands/vcl/custom/list.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List the uploaded VCLs for a particular service and version") @@ -35,7 +34,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/custom/update.go b/pkg/commands/vcl/custom/update.go index df0a8d5f0..18f9aa2fd 100644 --- a/pkg/commands/vcl/custom/update.go +++ b/pkg/commands/vcl/custom/update.go @@ -9,17 +9,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update the uploaded VCL for a particular service and version") @@ -42,7 +40,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -61,7 +59,6 @@ type UpdateCommand struct { autoClone cmd.OptionalAutoClone content cmd.OptionalString - manifest manifest.Data name string newName cmd.OptionalString serviceName cmd.OptionalServiceNameID @@ -73,7 +70,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/snippet/create.go b/pkg/commands/vcl/snippet/create.go index 6114e041e..fedd93951 100644 --- a/pkg/commands/vcl/snippet/create.go +++ b/pkg/commands/vcl/snippet/create.go @@ -3,24 +3,23 @@ package snippet import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // Locations is a list of VCL subroutines. var Locations = []string{"init", "recv", "hash", "hit", "miss", "pass", "fetch", "error", "deliver", "log", "none"} // NewCreateCommand returns a usable command registered under the parent. -func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *CreateCommand { +func NewCreateCommand(parent cmd.Registerer, g *global.Data) *CreateCommand { c := CreateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("create", "Create a snippet for a particular service and version").Alias("add") @@ -45,7 +44,7 @@ func NewCreateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *C c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -67,7 +66,6 @@ type CreateCommand struct { content cmd.OptionalString dynamic cmd.OptionalBool location cmd.OptionalString - manifest manifest.Data name cmd.OptionalString priority cmd.OptionalInt serviceName cmd.OptionalServiceNameID @@ -79,7 +77,7 @@ func (c *CreateCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/snippet/delete.go b/pkg/commands/vcl/snippet/delete.go index 911be7d8f..552d1acb9 100644 --- a/pkg/commands/vcl/snippet/delete.go +++ b/pkg/commands/vcl/snippet/delete.go @@ -3,21 +3,20 @@ package snippet import ( "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewDeleteCommand returns a usable command registered under the parent. -func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DeleteCommand { +func NewDeleteCommand(parent cmd.Registerer, g *global.Data) *DeleteCommand { c := DeleteCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("delete", "Delete a specific snippet for a particular service and version").Alias("remove") @@ -38,7 +37,7 @@ func NewDeleteCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *D c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -56,7 +55,6 @@ type DeleteCommand struct { cmd.Base autoClone cmd.OptionalAutoClone - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -67,7 +65,7 @@ func (c *DeleteCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/snippet/describe.go b/pkg/commands/vcl/snippet/describe.go index ccc69fc0c..baf1b2ba0 100644 --- a/pkg/commands/vcl/snippet/describe.go +++ b/pkg/commands/vcl/snippet/describe.go @@ -9,16 +9,14 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" ) // NewDescribeCommand returns a usable command registered under the parent. -func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *DescribeCommand { +func NewDescribeCommand(parent cmd.Registerer, g *global.Data) *DescribeCommand { c := DescribeCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("describe", "Get the uploaded VCL snippet for a particular service and version").Alias("get") @@ -37,7 +35,7 @@ func NewDescribeCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -57,7 +55,6 @@ type DescribeCommand struct { cmd.JSONOutput dynamic cmd.OptionalBool - manifest manifest.Data name string serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion @@ -73,7 +70,7 @@ func (c *DescribeCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/snippet/list.go b/pkg/commands/vcl/snippet/list.go index 2a1d1d830..3c1e22ca0 100644 --- a/pkg/commands/vcl/snippet/list.go +++ b/pkg/commands/vcl/snippet/list.go @@ -4,21 +4,20 @@ import ( "fmt" "io" + "github.com/fastly/go-fastly/v8/fastly" + "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" - "github.com/fastly/go-fastly/v8/fastly" ) // NewListCommand returns a usable command registered under the parent. -func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *ListCommand { +func NewListCommand(parent cmd.Registerer, g *global.Data) *ListCommand { c := ListCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("list", "List the uploaded VCL snippets for a particular service and version") @@ -35,7 +34,7 @@ func NewListCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *Lis c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -53,7 +52,6 @@ type ListCommand struct { cmd.Base cmd.JSONOutput - manifest manifest.Data serviceName cmd.OptionalServiceNameID serviceVersion cmd.OptionalServiceVersion } @@ -67,7 +65,7 @@ func (c *ListCommand) Exec(_ io.Reader, out io.Writer) error { serviceID, serviceVersion, err := cmd.ServiceDetails(cmd.ServiceDetailsOpts{ AllowActiveLocked: true, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/vcl/snippet/snippet_test.go b/pkg/commands/vcl/snippet/snippet_test.go index 6edc2f22d..bfd5062d1 100644 --- a/pkg/commands/vcl/snippet/snippet_test.go +++ b/pkg/commands/vcl/snippet/snippet_test.go @@ -8,6 +8,7 @@ import ( "github.com/fastly/go-fastly/v8/fastly" "github.com/fastly/cli/pkg/app" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/testutil" ) @@ -193,8 +194,8 @@ func TestVCLSnippetCreate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -273,8 +274,8 @@ func TestVCLSnippetDelete(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -358,8 +359,8 @@ func TestVCLSnippetDescribe(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -427,8 +428,8 @@ func TestVCLSnippetList(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } @@ -571,8 +572,8 @@ func TestVCLSnippetUpdate(t *testing.T) { testcase := &scenarios[testcaseIdx] t.Run(testcase.Name, func(t *testing.T) { var stdout bytes.Buffer - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { - opts := testutil.NewRunOpts(testcase.Args, &stdout) + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { + opts := testutil.MockGlobalData(testcase.Args, &stdout) opts.APIClientFactory = mock.APIClient(testcase.API) return opts, nil } diff --git a/pkg/commands/vcl/snippet/update.go b/pkg/commands/vcl/snippet/update.go index fa9d444f4..b62c1c0a1 100644 --- a/pkg/commands/vcl/snippet/update.go +++ b/pkg/commands/vcl/snippet/update.go @@ -9,17 +9,15 @@ import ( "github.com/fastly/cli/pkg/cmd" fsterr "github.com/fastly/cli/pkg/errors" "github.com/fastly/cli/pkg/global" - "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/text" ) // NewUpdateCommand returns a usable command registered under the parent. -func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *UpdateCommand { +func NewUpdateCommand(parent cmd.Registerer, g *global.Data) *UpdateCommand { c := UpdateCommand{ Base: cmd.Base{ Globals: g, }, - manifest: m, } c.CmdClause = parent.Command("update", "Update a VCL snippet for a particular service and version") @@ -44,7 +42,7 @@ func NewUpdateCommand(parent cmd.Registerer, g *global.Data, m manifest.Data) *U c.RegisterFlag(cmd.StringFlagOpts{ Name: cmd.FlagServiceIDName, Description: cmd.FlagServiceIDDesc, - Dst: &c.manifest.Flag.ServiceID, + Dst: &g.Manifest.Flag.ServiceID, Short: 's', }) c.RegisterFlag(cmd.StringFlagOpts{ @@ -69,7 +67,6 @@ type UpdateCommand struct { content cmd.OptionalString dynamic cmd.OptionalBool location cmd.OptionalString - manifest manifest.Data name string newName cmd.OptionalString priority cmd.OptionalInt @@ -84,7 +81,7 @@ func (c *UpdateCommand) Exec(_ io.Reader, out io.Writer) error { AllowActiveLocked: c.dynamic.WasSet && c.dynamic.Value, AutoCloneFlag: c.autoClone, APIClient: c.Globals.APIClient, - Manifest: c.manifest, + Manifest: *c.Globals.Manifest, Out: out, ServiceNameFlag: c.serviceName, ServiceVersionFlag: c.serviceVersion, diff --git a/pkg/commands/version/root.go b/pkg/commands/version/root.go index e03485af6..5b72d78da 100644 --- a/pkg/commands/version/root.go +++ b/pkg/commands/version/root.go @@ -11,6 +11,7 @@ import ( "github.com/fastly/cli/pkg/cmd" "github.com/fastly/cli/pkg/github" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/revision" "github.com/fastly/cli/pkg/useragent" ) @@ -27,13 +28,15 @@ func init() { // It should be installed under the primary root command. type RootCommand struct { cmd.Base - viceroyVersioner github.AssetVersioner } // NewRootCommand returns a new command registered in the parent. -func NewRootCommand(parent cmd.Registerer, viceroyVersioner github.AssetVersioner) *RootCommand { - var c RootCommand - c.viceroyVersioner = viceroyVersioner +func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { + c := RootCommand{ + Base: cmd.Base{ + Globals: g, + }, + } c.CmdClause = parent.Command("version", "Display version information for the Fastly CLI") return &c } @@ -43,7 +46,7 @@ func (c *RootCommand) Exec(_ io.Reader, out io.Writer) error { fmt.Fprintf(out, "Fastly CLI version %s (%s)\n", revision.AppVersion, revision.GitCommit) fmt.Fprintf(out, "Built with %s\n", revision.GoVersion) - viceroy := filepath.Join(github.InstallDir, c.viceroyVersioner.BinaryName()) + viceroy := filepath.Join(github.InstallDir, c.Globals.Versioners.Viceroy.BinaryName()) // gosec flagged this: // G204 (CWE-78): Subprocess launched with variable // Disabling as we lookup the binary in a trusted location. For this to be a diff --git a/pkg/commands/version/version_test.go b/pkg/commands/version/version_test.go index 6ad86623d..2cdaa9077 100644 --- a/pkg/commands/version/version_test.go +++ b/pkg/commands/version/version_test.go @@ -12,6 +12,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/github" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/testutil" ) @@ -67,15 +68,15 @@ func TestVersion(t *testing.T) { var stdout bytes.Buffer args := testutil.Args("version") - opts := testutil.NewRunOpts(args, &stdout) - opts.Versioners = app.Versioners{ + opts := testutil.MockGlobalData(args, &stdout) + opts.Versioners = global.Versioners{ Viceroy: github.New(github.Opts{ Org: "fastly", Repo: "viceroy", Binary: "viceroy", }), } - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err = app.Run(args, nil) diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index ed10c451f..0ba6e8c94 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -14,6 +14,7 @@ import ( "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/config" "github.com/fastly/cli/pkg/env" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/testutil" ) @@ -73,14 +74,14 @@ func TestWhoami(t *testing.T) { } { t.Run(testcase.name, func(t *testing.T) { var stdout bytes.Buffer - opts := testutil.NewRunOpts(testcase.args, &stdout) + opts := testutil.MockGlobalData(testcase.args, &stdout) opts.Env = testcase.env opts.HTTPClient = testcase.client - app.Init = func(_ []string, _ io.Reader) (app.RunOpts, error) { + app.Init = func(_ []string, _ io.Reader) (*global.Data, error) { return opts, nil } err := app.Run(testcase.args, nil) - opts.ConfigFile = config.File{} + opts.Config = config.File{} t.Log(stdout.String()) testutil.AssertErrorContains(t, err, testcase.wantError) testutil.AssertStringContains(t, stdout.String(), testcase.wantOutput) diff --git a/pkg/global/doc.go b/pkg/global/doc.go index e81c1e744..08ccb9a68 100644 --- a/pkg/global/doc.go +++ b/pkg/global/doc.go @@ -1,2 +1,3 @@ -// Package global defines data structures that are used globally. +// Package global exposes a type to contain global'ish data. +// Effectively we use it to avoid an unfortunate import loop issue. package global diff --git a/pkg/global/global.go b/pkg/global/global.go index 889202c3f..2091fa25b 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -4,8 +4,10 @@ import ( "io" "github.com/fastly/cli/pkg/api" + "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/config" fsterr "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/github" "github.com/fastly/cli/pkg/lookup" "github.com/fastly/cli/pkg/manifest" ) @@ -16,6 +18,20 @@ const DefaultAPIEndpoint = "https://api.fastly.com" // DefaultAccountEndpoint is the default Fastly Accounts endpoint. const DefaultAccountEndpoint = "https://accounts.fastly.com" +// APIClientFactory creates a Fastly API client (modeled as an api.Interface) +// from a user-provided API token. It exists as a type in order to parameterize +// the Run helper with it: in the real CLI, we can use NewClient from the Fastly +// API client library via RealClient; in tests, we can provide a mock API +// interface via MockClient. +type APIClientFactory func(token, apiEndpoint string, debugMode bool) (api.Interface, error) + +// Versioners represents all supported versioner types. +type Versioners struct { + CLI github.AssetVersioner + Viceroy github.AssetVersioner + WasmTools github.AssetVersioner +} + // Data holds global-ish configuration data from all sources: environment // variables, config files, and flags. It has methods to give each parameter to // the components that need it, including the place the parameter came from, @@ -30,35 +46,46 @@ const DefaultAccountEndpoint = "https://accounts.fastly.com" // (e.g. an email address). Otherwise, parameters should be defined in specific // command structs, and parsed as flags. type Data struct { - // Env is all the data that is provided by the environment. - Env config.Environment + // APIClient is a Fastly API client instance. + APIClient api.Interface + // APIClientFactory is a factory function for creating an api.Interface type. + APIClientFactory APIClientFactory + // Args are the command line arguments provided by the user. + Args []string + // AuthServer is an instance of the authentication server type. + // Used for interacting with Fastly's SSO/OAuth authentication provider. + AuthServer auth.Runner // Config is an instance of the CLI configuration data. Config config.File // ConfigPath is the path to the CLI's application configuration. ConfigPath string + // Env is all the data that is provided by the environment. + Env config.Environment + // ErrLog provides an interface for recording errors to disk. + ErrLog fsterr.LogInterface // ExecuteWasmTools is a function that executes the wasm-tools binary. ExecuteWasmTools func(bin string, args []string) error // Flags are all the global CLI flags. Flags Flags - // Manifest is the fastly.toml manifest file. - Manifest manifest.Data + // HTTPClient is a HTTP client. + HTTPClient api.HTTPClient + // Input is the standard input for accepting input from the user. + Input io.Reader + // Manifest represents the fastly.toml manifest file and associated flags. + Manifest *manifest.Data + // Opener is a function that can open a browser window. + Opener func(string) error // Output is the output for displaying information (typically os.Stdout) Output io.Writer + // RTSClient is a Fastly API client instance for the Real Time Stats endpoints. + RTSClient api.RealtimeStatsInterface // SkipAuthPrompt is used to indicate to the `sso` command that the // interactive prompt can be skipped. This is for scenarios where the command // is executed directly by the user. SkipAuthPrompt bool - - // Custom interfaces - - // ErrLog provides an interface for recording errors to disk. - ErrLog fsterr.LogInterface - // APIClient is a Fastly API client instance. - APIClient api.Interface - // HTTPClient is a HTTP client. - HTTPClient api.HTTPClient - // RTSClient is a Fastly API client instance for the Real Time Stats endpoints. - RTSClient api.RealtimeStatsInterface + // Versioners contains multiple software versioning checkers. + // e.g. Check for latest CLI or Viceroy version. + Versioners Versioners } // Token yields the Fastly API token. diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index 14979c72a..d0a1f7f3f 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -7,12 +7,10 @@ import ( "strings" "time" - "github.com/hashicorp/cap/oidc" - - "github.com/fastly/cli/pkg/app" "github.com/fastly/cli/pkg/auth" "github.com/fastly/cli/pkg/config" "github.com/fastly/cli/pkg/errors" + "github.com/fastly/cli/pkg/global" "github.com/fastly/cli/pkg/manifest" "github.com/fastly/cli/pkg/mock" "github.com/fastly/cli/pkg/runtime" @@ -58,11 +56,17 @@ func Args(args string) []string { // MockAuthServer is used to no-op the authentication server. type MockAuthServer struct { - auth.Starter + auth.Runner Result chan auth.AuthorizationResult } +// AuthURL returns a fully qualified authorization_endpoint. +// i.e. path + audience + scope + code_challenge etc. +func (s MockAuthServer) AuthURL() (string, error) { + return "", nil // no-op +} + // GetResult returns the results channel func (s MockAuthServer) GetResult() chan auth.AuthorizationResult { return s.Result @@ -78,20 +82,17 @@ func (s MockAuthServer) SetAPIEndpoint(_ string) { // no-op } -// SetVerifier sets the code verifier. -func (s MockAuthServer) SetVerifier(_ *oidc.S256Verifier) { - // no-op -} - // Start starts a local server for handling authentication processing. func (s MockAuthServer) Start() error { return nil // no-op } -// NewRunOpts returns a struct that can be used to populate a call to app.Run() +// MockGlobalData returns a struct that can be used to populate a call to app.Exec() // while the majority of fields will be pre-populated and only those fields // commonly changed for testing purposes will need to be provided. -func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { +// +// TODO: Move this and other mocks into mocks package. +func MockGlobalData(args []string, stdout io.Writer) *global.Data { var md manifest.Data md.File.Args = args md.File.SetErrLog(errors.Log) @@ -103,11 +104,11 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { configPath = "NUL" } - return app.RunOpts{ + return &global.Data{ Args: args, APIClientFactory: mock.APIClient(mock.API{}), AuthServer: &MockAuthServer{}, - ConfigFile: config.File{ + Config: config.File{ Profiles: TokenProfile(), }, ConfigPath: configPath, @@ -121,7 +122,7 @@ func NewRunOpts(args []string, stdout io.Writer) app.RunOpts { Opener: func(input string) error { return nil // no-op }, - Stdout: stdout, + Output: stdout, } } From 3d4b24c144d5cccb5549326045bbef8286adc06b Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 11:35:54 +0000 Subject: [PATCH 105/115] fix: move functions inside auth server + set api endpoint --- pkg/app/run.go | 28 ++-- pkg/auth/auth.go | 275 +++++++++++++++++++-------------------- pkg/commands/sso/root.go | 5 - 3 files changed, 151 insertions(+), 157 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index d86b6df54..a2e49f6f7 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -206,8 +206,8 @@ var Init = func(args []string, stdin io.Reader) (*global.Data, error) { // and returned to the caller. This includes usage text. func Exec(data *global.Data) error { app := configureKingpin(data) - commands := commands.Define(app, data) - command, commandName, err := processCommandInput(data, app, commands) + cmds := commands.Define(app, data) + command, commandName, err := processCommandInput(data, app, cmds) if err != nil { return err } @@ -248,7 +248,12 @@ func Exec(data *global.Data) error { displayAPIEndpoint(apiEndpoint, endpointSource, data.Output) } - token, err := processToken(commands, commandName, data) + // NOTE: We need the AuthServer setter method due to assignment data races. + // i.e. app.Init() doesn't have access to Kingpin flag values yet. + // The flags are only parsed/assigned via configureKingpin(). + data.AuthServer.SetAPIEndpoint(apiEndpoint) + + token, err := processToken(cmds, commandName, data) if err != nil { return fmt.Errorf("failed to process token: %w", err) } @@ -322,7 +327,7 @@ func configureKingpin(data *global.Data) *kingpin.Application { // // Finally, we check if there is a profile override in place (e.g. set via the // --profile flag or using the `profile` field in the fastly.toml manifest). -func processToken(commands []cmd.Command, commandName string, data *global.Data) (token string, err error) { +func processToken(cmds []cmd.Command, commandName string, data *global.Data) (token string, err error) { warningMessage := "No API token could be found" var tokenSource lookup.Source token, tokenSource = data.Token() @@ -336,7 +341,7 @@ func processToken(commands []cmd.Command, commandName string, data *global.Data) // If there's no token available, and we need one for the invoked command, // then we'll trigger the SSO authentication flow. if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { - token, tokenSource, err = ssoAuthentication(tokenSource, token, warningMessage, commands, data) + token, tokenSource, err = ssoAuthentication(tokenSource, token, warningMessage, cmds, data) if err != nil { return token, fmt.Errorf("failed to check profile token: %w", err) } @@ -417,15 +422,13 @@ func checkProfileToken( if data.Flags.Verbose { text.Info(data.Output, "Your access token has now expired. We will attempt to refresh it") } - accountEndpoint, _ := data.AccountEndpoint() - apiEndpoint, _ := data.APIEndpoint() - updatedJWT, err := auth.RefreshAccessToken(accountEndpoint, profileData.RefreshToken) + updatedJWT, err := data.AuthServer.RefreshAccessToken(profileData.RefreshToken) if err != nil { return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) } - email, at, err := auth.ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, updatedJWT.AccessToken, data.Env.DebugMode, data.HTTPClient) + email, at, err := data.AuthServer.ValidateAndRetrieveAPIToken(updatedJWT.AccessToken) if err != nil { return tokenSource, warningMessage, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) } @@ -516,13 +519,16 @@ func forceReAuth() lookup.Source { func ssoAuthentication( tokenSource lookup.Source, token, warningMessage string, - commands []cmd.Command, + cmds []cmd.Command, data *global.Data, ) (string, lookup.Source, error) { - for _, command := range commands { + for _, command := range cmds { commandName := strings.Split(command.Name(), " ")[0] if commandName == "sso" { if !data.Flags.AutoYes && !data.Flags.NonInteractive { + if data.Verbose() { + text.Break(data.Output) + } text.Important(data.Output, "%s. We need to open your browser to authenticate you.", warningMessage) text.Break(data.Output) cont, err := text.AskYesNo(data.Output, text.BoldYellow("Do you want to continue? [y/N]: "), data.Input) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 7378e038d..6528fad82 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -49,12 +49,16 @@ type Runner interface { AuthURL() (string, error) // GetResult returns the results channel GetResult() chan AuthorizationResult - // SetAccountEndpoint sets the account endpoint. - SetAccountEndpoint(endpoint string) + // RefreshAccessToken constructs and calls the token_endpoint with the + // refresh token so we can refresh and return the access token. + RefreshAccessToken(refreshToken string) (JWT, error) // SetEndpoint sets the API endpoint. SetAPIEndpoint(endpoint string) // Start starts a local server for handling authentication processing. Start() error + // ValidateAndRetrieveAPIToken verifies the signature and the claims and + // exchanges the access token for an API token. + ValidateAndRetrieveAPIToken(accessToken string) (string, *APIToken, error) } // Server is a local server responsible for authentication processing. @@ -101,9 +105,46 @@ func (s Server) GetResult() chan AuthorizationResult { return s.Result } -// SetAccountEndpoint sets the account endpoint. -func (s *Server) SetAccountEndpoint(endpoint string) { - s.AccountEndpoint = endpoint +// GetJWT constructs and calls the token_endpoint path, returning a JWT +// containing the access and refresh tokens and associated TTLs. +func (s Server) GetJWT(authorizationCode string) (JWT, error) { + payload := fmt.Sprintf( + "grant_type=authorization_code&client_id=%s&code_verifier=%s&code=%s&redirect_uri=%s", + ClientID, + s.Verifier.Verifier(), + authorizationCode, + "http://localhost:8080/callback", // NOTE: not redirected to, just a security check. + ) + + req, err := http.NewRequest("POST", s.WellKnownEndpoints.Token, strings.NewReader(payload)) + if err != nil { + return JWT{}, err + } + + req.Header.Add("content-type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return JWT{}, err + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return JWT{}, fmt.Errorf("failed to exchange code for jwt (status: %s)", res.Status) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return JWT{}, err + } + + var j JWT + err = json.Unmarshal(body, &j) + if err != nil { + return JWT{}, err + } + + return j, nil } // SetAPIEndpoint sets the API endpoint. @@ -149,8 +190,7 @@ func (s *Server) HandleCallback() http.HandlerFunc { // Exchange the authorization code and the code verifier for a JWT. // NOTE: I use the identifier `j` to avoid overlap with the `jwt` package. - codeVerifier := s.Verifier.Verifier() - j, err := GetJWT(s.AccountEndpoint, codeVerifier, authorizationCode) + j, err := s.GetJWT(authorizationCode) if err != nil || j.AccessToken == "" || j.IDToken == "" { fmt.Fprint(w, "ERROR: failed to exchange code for JWT\n") s.Result <- AuthorizationResult{ @@ -159,7 +199,7 @@ func (s *Server) HandleCallback() http.HandlerFunc { return } - email, at, err := ValidateAndRetrieveAPIToken(s.AccountEndpoint, s.APIEndpoint, j.AccessToken, s.DebugMode, s.HTTPClient) + email, at, err := s.ValidateAndRetrieveAPIToken(j.AccessToken) if err != nil { s.Result <- AuthorizationResult{ Err: err, @@ -180,8 +220,8 @@ func (s *Server) HandleCallback() http.HandlerFunc { // exchanges the access token for an API token. // // NOTE: This function exists as it's called by this package + app.Run(). -func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debugMode string, httpClient api.HTTPClient) (string, *APIToken, error) { - claims, err := VerifyJWTSignature(accountEndpoint, accessToken) +func (s *Server) ValidateAndRetrieveAPIToken(accessToken string) (string, *APIToken, error) { + claims, err := s.VerifyJWTSignature(accessToken) if err != nil { return "", nil, err } @@ -201,9 +241,9 @@ func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debu return "", nil, errors.New("failed to extract aud from JWT claims") } - if aud != apiEndpoint { + if aud != s.APIEndpoint { if !ok { - return "", nil, fmt.Errorf("failed to match expected aud: %s", apiEndpoint) + return "", nil, fmt.Errorf("failed to match expected aud: %s", s.APIEndpoint) } } @@ -213,7 +253,7 @@ func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debu } // Exchange the access token for a Fastly API token. - at, err := ExchangeAccessToken(accessToken, apiEndpoint, httpClient, debugMode) + at, err := s.ExchangeAccessToken(accessToken) if err != nil { return "", nil, fmt.Errorf("failed to exchange access token for an API token: %w", err) } @@ -225,129 +265,69 @@ func ValidateAndRetrieveAPIToken(accountEndpoint, apiEndpoint, accessToken, debu return e, at, nil } -// APIToken is returned from the /login-enhanced endpoint. -type APIToken struct { - // AccessToken is used to access the Fastly API. - AccessToken string `json:"access_token"` - // CustomerID is the customer ID. - CustomerID string `json:"customer_id"` - // ExpiresAt is when the access token will expire. - ExpiresAt string `json:"expires_at"` - // ID is a unique ID. - ID string `json:"id"` - // Name is a description of the token. - Name string `json:"name"` - // UserID is the user's ID. - UserID string `json:"user_id"` -} - -// AuthorizationResult represents the result of the authorization process. -type AuthorizationResult struct { - // Email address extracted from JWT claims. - Email string - // Err is any error received during authentication. - Err error - // Jwt is the JWT token returned by the authorization server. - Jwt JWT - // SessionToken is a temporary API token. - SessionToken string -} - -// GetJWT constructs and calls the token_endpoint path, returning a JWT -// containing the access and refresh tokens and associated TTLs. -func GetJWT(accountEndpoint, codeVerifier, authorizationCode string) (JWT, error) { - path := "/realms/fastly/protocol/openid-connect/token" - - payload := fmt.Sprintf( - "grant_type=authorization_code&client_id=%s&code_verifier=%s&code=%s&redirect_uri=%s", - ClientID, - codeVerifier, - authorizationCode, - "http://localhost:8080/callback", // NOTE: not redirected to, just a security check. - ) - - req, err := http.NewRequest("POST", accountEndpoint+path, strings.NewReader(payload)) - if err != nil { - return JWT{}, err - } - - req.Header.Add("content-type", "application/x-www-form-urlencoded") - - res, err := http.DefaultClient.Do(req) - if err != nil { - return JWT{}, err - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return JWT{}, fmt.Errorf("failed to exchange code for jwt (status: %s)", res.Status) - } +// VerifyJWTSignature calls the jwks_uri endpoint and extracts its claims. +func (s *Server) VerifyJWTSignature(accessToken string) (claims map[string]any, err error) { + ctx := context.Background() - body, err := io.ReadAll(res.Body) + // NOTE: The last argument is optional and is for validating the JWKs endpoint + // (which we don't need to do, so we pass an empty string) + keySet, err := jwt.NewJSONWebKeySet(ctx, s.WellKnownEndpoints.Certs, "") if err != nil { - return JWT{}, err + return claims, fmt.Errorf("failed to verify signature of access token: %w", err) } - var j JWT - err = json.Unmarshal(body, &j) + claims, err = keySet.VerifySignature(ctx, accessToken) if err != nil { - return JWT{}, err + return nil, fmt.Errorf("failed to verify signature of access token: %w", err) } - return j, nil -} - -// JWT is the API response for a Token request. -// -// Access Token typically has a TTL of 5mins. -// Refresh Token typically has a TTL of 30mins. -type JWT struct { - // AccessToken can be exchanged for a Fastly API token. - AccessToken string `json:"access_token"` - // ExpiresIn indicates the lifetime (in seconds) of the access token. - ExpiresIn int `json:"expires_in"` - // IDToken contains user information that must be decoded and extracted. - IDToken string `json:"id_token"` - // RefreshExpiresIn indicates the lifetime (in seconds) of the refresh token. - RefreshExpiresIn int `json:"refresh_expires_in"` - // RefreshToken contains a token used to refresh the issued access token. - RefreshToken string `json:"refresh_token"` - // TokenType indicates which HTTP authentication scheme is used (e.g. Bearer). - TokenType string `json:"token_type"` + return claims, nil } -// VerifyJWTSignature calls the jwks_uri endpoint and extracts its claims. -func VerifyJWTSignature(accountEndpoint, token string) (claims map[string]any, err error) { - ctx := context.Background() - path := "/realms/fastly/protocol/openid-connect/certs" - - // NOTE: The last argument is optional and is for validating the JWKs endpoint - // (which we don't need to do, so we pass an empty string) - keySet, err := jwt.NewJSONWebKeySet(ctx, accountEndpoint+path, "") +// ExchangeAccessToken exchanges `accessToken` for a Fastly API token. +func (s *Server) ExchangeAccessToken(accessToken string) (*APIToken, error) { + debug, _ := strconv.ParseBool(s.DebugMode) + resp, err := undocumented.Call(undocumented.CallOptions{ + APIEndpoint: s.APIEndpoint, + HTTPClient: s.HTTPClient, + HTTPHeaders: []undocumented.HTTPHeader{ + { + Key: "Authorization", + Value: fmt.Sprintf("Bearer %s", accessToken), + }, + }, + Method: http.MethodPost, + Path: "/login-enhanced", + Debug: debug, + }) if err != nil { - return claims, fmt.Errorf("failed to verify signature of access token: %w", err) + if apiErr, ok := err.(undocumented.APIError); ok { + if apiErr.StatusCode != http.StatusConflict { + err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) + } + } + return nil, err } - claims, err = keySet.VerifySignature(ctx, token) + at := &APIToken{} + err = json.Unmarshal(resp, at) if err != nil { - return nil, fmt.Errorf("failed to verify signature of access token: %w", err) + return nil, fmt.Errorf("failed to unmarshal json containing API token: %w", err) } - return claims, nil + return at, nil } // RefreshAccessToken constructs and calls the token_endpoint with the // refresh token so we can refresh and return the access token. -func RefreshAccessToken(accountEndpoint, refreshToken string) (JWT, error) { - path := "/realms/fastly/protocol/openid-connect/token" - +func (s *Server) RefreshAccessToken(refreshToken string) (JWT, error) { payload := fmt.Sprintf( "grant_type=refresh_token&client_id=%s&refresh_token=%s", ClientID, refreshToken, ) - req, err := http.NewRequest("POST", accountEndpoint+path, strings.NewReader(payload)) + req, err := http.NewRequest("POST", s.WellKnownEndpoints.Token, strings.NewReader(payload)) if err != nil { return JWT{}, err } @@ -378,38 +358,51 @@ func RefreshAccessToken(accountEndpoint, refreshToken string) (JWT, error) { return j, nil } -// ExchangeAccessToken exchanges `accessToken` for a Fastly API token. -func ExchangeAccessToken(accessToken, apiEndpoint string, httpClient api.HTTPClient, debugMode string) (*APIToken, error) { - debug, _ := strconv.ParseBool(debugMode) - resp, err := undocumented.Call(undocumented.CallOptions{ - APIEndpoint: apiEndpoint, - HTTPClient: httpClient, - HTTPHeaders: []undocumented.HTTPHeader{ - { - Key: "Authorization", - Value: fmt.Sprintf("Bearer %s", accessToken), - }, - }, - Method: http.MethodPost, - Path: "/login-enhanced", - Debug: debug, - }) - if err != nil { - if apiErr, ok := err.(undocumented.APIError); ok { - if apiErr.StatusCode != http.StatusConflict { - err = fmt.Errorf("%w: %d %s", err, apiErr.StatusCode, http.StatusText(apiErr.StatusCode)) - } - } - return nil, err - } +// APIToken is returned from the /login-enhanced endpoint. +type APIToken struct { + // AccessToken is used to access the Fastly API. + AccessToken string `json:"access_token"` + // CustomerID is the customer ID. + CustomerID string `json:"customer_id"` + // ExpiresAt is when the access token will expire. + ExpiresAt string `json:"expires_at"` + // ID is a unique ID. + ID string `json:"id"` + // Name is a description of the token. + Name string `json:"name"` + // UserID is the user's ID. + UserID string `json:"user_id"` +} - at := &APIToken{} - err = json.Unmarshal(resp, at) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal json containing API token: %w", err) - } +// AuthorizationResult represents the result of the authorization process. +type AuthorizationResult struct { + // Email address extracted from JWT claims. + Email string + // Err is any error received during authentication. + Err error + // Jwt is the JWT token returned by the authorization server. + Jwt JWT + // SessionToken is a temporary API token. + SessionToken string +} - return at, nil +// JWT is the API response for a Token request. +// +// Access Token typically has a TTL of 5mins. +// Refresh Token typically has a TTL of 30mins. +type JWT struct { + // AccessToken can be exchanged for a Fastly API token. + AccessToken string `json:"access_token"` + // ExpiresIn indicates the lifetime (in seconds) of the access token. + ExpiresIn int `json:"expires_in"` + // IDToken contains user information that must be decoded and extracted. + IDToken string `json:"id_token"` + // RefreshExpiresIn indicates the lifetime (in seconds) of the refresh token. + RefreshExpiresIn int `json:"refresh_expires_in"` + // RefreshToken contains a token used to refresh the issued access token. + RefreshToken string `json:"refresh_token"` + // TokenType indicates which HTTP authentication scheme is used (e.g. Bearer). + TokenType string `json:"token_type"` } // TokenExpired indicates if the specified TTL has past. diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index b9451004d..6b66cc55f 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -67,11 +67,6 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { } } - accountEndpoint, _ := c.Globals.AccountEndpoint() - apiEndpoint, _ := c.Globals.APIEndpoint() - c.Globals.AuthServer.SetAccountEndpoint(accountEndpoint) - c.Globals.AuthServer.SetAPIEndpoint(apiEndpoint) - var serverErr error go func() { err := c.Globals.AuthServer.Start() From 2cc61b7dd70e7d13b72afcd08e0b4aee45e97194 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 11:36:14 +0000 Subject: [PATCH 106/115] remove(testutil): SetAccountEndpoint mock method --- pkg/testutil/args.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/testutil/args.go b/pkg/testutil/args.go index d0a1f7f3f..3eeefaada 100644 --- a/pkg/testutil/args.go +++ b/pkg/testutil/args.go @@ -72,11 +72,6 @@ func (s MockAuthServer) GetResult() chan auth.AuthorizationResult { return s.Result } -// SetAccountEndpoint sets the account endpoint. -func (s MockAuthServer) SetAccountEndpoint(_ string) { - // no-op -} - // SetAPIEndpoint sets the API endpoint. func (s MockAuthServer) SetAPIEndpoint(_ string) { // no-op From 1dad712997a1ae81b405a0ceeda34c2d0455efef Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 11:58:24 +0000 Subject: [PATCH 107/115] fix: stop processing if user doesn't want to continue --- pkg/app/run.go | 6 +++++- pkg/errors/errors.go | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index a2e49f6f7..024cfd185 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -2,6 +2,7 @@ package app import ( "encoding/json" + "errors" "fmt" "io" "net/http" @@ -255,6 +256,9 @@ func Exec(data *global.Data) error { token, err := processToken(cmds, commandName, data) if err != nil { + if errors.Is(err, fsterr.ErrDontContinue) { + return nil // we shouldn't exit 1 if user chooses to stop + } return fmt.Errorf("failed to process token: %w", err) } @@ -537,7 +541,7 @@ func ssoAuthentication( return token, tokenSource, err } if !cont { - return token, tokenSource, nil + return token, tokenSource, fsterr.ErrDontContinue } } diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 9419e28c6..33a634c44 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -15,6 +15,9 @@ var ErrSignalKilled = fmt.Errorf("a SIGTERM was received") // file modification noticed while running `compute serve --watch`. var ErrViceroyRestart = fmt.Errorf("a RESTART was initiated") +// ErrDontContinue means the user said "NO" when prompted whether to continue. +var ErrDontContinue = fmt.Errorf("will not continue") + // ErrIncompatibleServeFlags means no --skip-build can't be used with --watch // because it defeats the purpose of --watch which is designed to restart // Viceroy whenever changes are detected (those changes would not be seen if we From 6822a0325cdd14ae6197ef0a9ec59431c714fc04 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 12:18:37 +0000 Subject: [PATCH 108/115] test: add DontWantOutput for TestSSO --- pkg/commands/sso/sso_test.go | 14 ++++++++------ pkg/testutil/scenarios.go | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index 03d106859..360fa7c84 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -163,14 +163,12 @@ func TestSSO(t *testing.T) { // We set an SSO token that has expired. // This allows us to validate the output message about expiration. // We don't respond "Y" to prompt to reauthenticate. - // But we've mocked the request to succeeds still so it doesn't matter. + // But we've mocked the request to succeed still so it doesn't matter. { TestScenario: testutil.TestScenario{ - Args: args("whoami"), - WantOutputs: []string{ - "Your access token has expired and so has your refresh token.", - "Alice Programmer ", - }, + Args: args("whoami"), + WantOutput: "Your access token has expired and so has your refresh token.", + DontWantOutput: "Alice Programmer ", }, ConfigFile: &config.File{ Profiles: config.Profiles{ @@ -326,6 +324,10 @@ func TestSSO(t *testing.T) { } else { testutil.AssertStringContains(t, stdout.String(), testcase.WantOutput) } + + for _, s := range testcase.DontWantOutputs { + testutil.AssertStringDoesntContain(t, stdout.String(), s) + } }) } } diff --git a/pkg/testutil/scenarios.go b/pkg/testutil/scenarios.go index 2befe9220..de5703602 100644 --- a/pkg/testutil/scenarios.go +++ b/pkg/testutil/scenarios.go @@ -4,10 +4,12 @@ import "github.com/fastly/cli/pkg/mock" // TestScenario represents a standard test case to be validated. type TestScenario struct { - API mock.API - Args []string - Name string - WantError string - WantOutput string - WantOutputs []string + API mock.API + Args []string + DontWantOutput string + DontWantOutputs []string + Name string + WantError string + WantOutput string + WantOutputs []string } From 2e53e0f4716bce638d685e2cb8266e3aea4ab735 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 14:12:53 +0000 Subject: [PATCH 109/115] feat: add --enable-sso flag --- pkg/app/run.go | 20 +++++++++++--------- pkg/app/usage.go | 8 +++++--- pkg/cmd/cmd.go | 6 ++++++ pkg/global/global.go | 31 +++++++++++++++++++++++-------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 024cfd185..375cb3a86 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -291,10 +291,10 @@ func configureKingpin(data *global.Data) *kingpin.Application { // error states and output control flow. app.Terminate(nil) - // WARNING: kingpin has no way of decorating flags as being "global" - // therefore if you add/remove a global flag you will also need to update - // the globalFlags map in pkg/app/usage.go which is used for usage rendering. - // You should also update `IsGlobalFlagsOnly` in ../cmd/cmd.go + // IMPORTANT: Kingpin doesn't support global flags. + // Any flags defined below must also be added to two other places: + // 1. ./usage.go (`globalFlags` map). + // 2. ../cmd/cmd.go (`IsGlobalFlagsOnly` function). // // NOTE: Global flags (long and short) MUST be unique. // A subcommand can't define a flag that is already global. @@ -304,8 +304,10 @@ func configureKingpin(data *global.Data) *kingpin.Application { app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&data.Flags.AcceptDefaults) app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&data.Flags.Account) app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&data.Flags.AutoYes) - // IMPORTANT: `--debug` is a built-in Kingpin flag so we can't use that. + // IMPORTANT: `--debug` is a built-in Kingpin flag so we must use `debug-mode`. app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&data.Flags.Debug) + // IMPORTANT: `--sso` causes a Kingpin runtime panic 🤦 so we use `enable-sso`. + app.Flag("enable-sso", "Enable Single-Sign On (SSO) for current profile execution (see also: 'fastly sso')").Hidden().BoolVar(&data.Flags.SSO) app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&data.Flags.Endpoint) app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&data.Flags.NonInteractive) app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&data.Flags.Profile) @@ -490,19 +492,19 @@ func checkProfileToken( // shouldSkipSSO identifies if a config is a pre-v5 config and, if it is, // informs the user how they can use the SSO flow. It checks if the SSO -// environment variable has been set and enables the SSO flow if so. +// environment variable (or flag) has been set and enables the SSO flow if so. func shouldSkipSSO(profileName string, pd *config.Profile, data *global.Data) bool { if noSSOToken(pd) { - return data.Env.UseSSO != "1" + return data.Env.UseSSO != "1" && !data.Flags.SSO // FIXME: Put back messaging once SSO is GA. - // if data.Env.UseSSO == "1" { + // if data.Env.UseSSO == "1" || data.Flags.SSO { // return false // don't skip SSO // } // if !data.Flags.Quiet { // if data.Flags.Verbose { // text.Break(data.Output) // } - // text.Important(data.Output, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, set `FASTLY_USE_SSO=1` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) + // text.Important(data.Output, "The Fastly API token used by the current '%s' profile is not a Fastly SSO (Single Sign-On) generated token. SSO-based tokens offer more security and convenience. To update your token, either set `FASTLY_USE_SSO=1` or pass `--enable-sso` before invoking the Fastly CLI. This will ensure the current profile is switched to using an SSO generated API token. Once the token has been switched over you no longer need to set `FASTLY_USE_SSO` for this profile (--token and FASTLY_API_TOKEN can still be used as overrides).\n\n", profileName) // } // return true // skip SSO } diff --git a/pkg/app/usage.go b/pkg/app/usage.go index c485dc1dc..75ca1fd1e 100644 --- a/pkg/app/usage.go +++ b/pkg/app/usage.go @@ -140,15 +140,17 @@ var UsageTemplateFuncs = template.FuncMap{ }, } -// WARNING: kingpin has no way of decorating flags as being "global" therefore -// if you add/remove a global flag you will also need to update the app.Flag() -// bindings in pkg/app/run.go. +// IMPORTANT: Kingpin doesn't support global flags. +// We hack a solution in ./run.go (`configureKingpin` function). // // NOTE: This map is used to help populate the CLI 'usage' template renderer. var globalFlags = map[string]bool{ "accept-defaults": true, + "account": true, "auto-yes": true, "debug-mode": true, + "enable-sso": true, + "endpoint": true, "help": true, "non-interactive": true, "profile": true, diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 2120ee5aa..48005f11f 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -253,13 +253,19 @@ func IsVerboseAndQuiet(args []string) bool { // // args: [--verbose -v --endpoint ... --token ... -t ... --endpoint ...] 10 // total: 10 +// +// IMPORTANT: Kingpin doesn't support global flags. +// We hack a solution in ../app/run.go (`configureKingpin` function). func IsGlobalFlagsOnly(args []string) bool { // Global flags are defined in ../app/run.go globals := map[string]int{ "--accept-defaults": 0, "-d": 0, + "--account": 1, "--auto-yes": 0, "-y": 0, + "--debug-mode": 0, + "--enable-sso": 0, "--endpoint": 1, "--help": 0, "--non-interactive": 0, diff --git a/pkg/global/global.go b/pkg/global/global.go index 2091fa25b..7a1eb973c 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -177,15 +177,30 @@ func (d *Data) AccountEndpoint() (string, lookup.Source) { // Flags represents all of the configuration parameters that can be set with // explicit flags. Consumers should bind their flag values to these fields // directly. +// +// IMPORTANT: Kingpin doesn't support global flags. +// We hack a solution in ../app/run.go (`configureKingpin` function). type Flags struct { + // AcceptDefaults auto-resolves prompts with a default defined. AcceptDefaults bool - Account string - AutoYes bool - Debug bool - Endpoint string + // Account is the authentication host address. + Account string + // AutoYes auto-resolves Yes/No prompts by answering "Yes". + AutoYes bool + // Debug enables the CLI's debug mode. + Debug bool + // Endpoint is the Fastly API address. + Endpoint string + // NonInteractive auto-resolves all prompts. NonInteractive bool - Profile string - Quiet bool - Token string - Verbose bool + // Profile indicates the profile to use (consequently the 'token' used). + Profile string + // Quiet silences all output except direct command output. + Quiet bool + // SSO enables to SSO authentication tokens for the current profile. + SSO bool + // Token is an override for a profile (when passed SSO is disabled). + Token string + // Verbose prints additional output. + Verbose bool } From fda9488210ed2c9e936ef5a73a4c0eb5d289f171 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 14:48:00 +0000 Subject: [PATCH 110/115] fix: don't os.Exit(1) for Yes/No --- cmd/fastly/main.go | 9 +++------ pkg/app/run.go | 2 +- pkg/commands/sso/root.go | 5 ++++- pkg/commands/sso/sso_test.go | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/fastly/main.go b/cmd/fastly/main.go index 19c224177..3b3c5c8ff 100644 --- a/cmd/fastly/main.go +++ b/cmd/fastly/main.go @@ -3,19 +3,17 @@ package main import ( "errors" - "io" "os" "github.com/fatih/color" "github.com/fastly/cli/pkg/app" fsterr "github.com/fastly/cli/pkg/errors" - "github.com/fastly/cli/pkg/text" ) func main() { if err := app.Run(os.Args, os.Stdin); err != nil { - if skipExit := processErr(err, os.Args, os.Stdout); skipExit { + if skipExit := processErr(err, os.Args); skipExit { return } os.Exit(1) @@ -23,7 +21,7 @@ func main() { } // processErr persists the error log to disk and deduces the error type. -func processErr(err error, args []string, out io.Writer) (skipExit bool) { +func processErr(err error, args []string) (skipExit bool) { // NOTE: We persist any error log entries to disk before attempting to handle // a possible error response from app.Run as there could be errors recorded // during the execution flow but were otherwise handled without bubbling an @@ -33,11 +31,10 @@ func processErr(err error, args []string, out io.Writer) (skipExit bool) { if logErr != nil { fsterr.Deduce(logErr).Print(color.Error) } - text.Break(out) - fsterr.Deduce(err).Print(color.Error) exitError := fsterr.SkipExitError{} if errors.As(err, &exitError) { return exitError.Skip } + fsterr.Deduce(err).Print(color.Error) return false } diff --git a/pkg/app/run.go b/pkg/app/run.go index 375cb3a86..c5c5a737b 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -426,7 +426,7 @@ func checkProfileToken( } if data.Flags.Verbose { - text.Info(data.Output, "Your access token has now expired. We will attempt to refresh it") + text.Info(data.Output, "\n\nYour access token has now expired. We will attempt to refresh it") } updatedJWT, err := data.AuthServer.RefreshAccessToken(profileData.RefreshToken) diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index 6b66cc55f..f8b7dbc9e 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -63,7 +63,10 @@ func (c *RootCommand) Exec(in io.Reader, out io.Writer) error { return err } if !cont { - return fmt.Errorf("user cancelled execution") + return fsterr.SkipExitError{ + Skip: true, + Err: fsterr.ErrDontContinue, + } } } diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index 360fa7c84..42329ae8a 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -35,7 +35,7 @@ func TestSSO(t *testing.T) { { TestScenario: testutil.TestScenario{ Args: args("sso"), - WantError: "user cancelled execution", + WantError: "will not continue", }, Stdin: []string{ "N", // when prompted to open a web browser to start authentication From 5160d5d47ffac191651cbd012832a736dbf40cd5 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 15:10:30 +0000 Subject: [PATCH 111/115] style(app): remove line break --- pkg/app/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index c5c5a737b..f43afe850 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -426,7 +426,7 @@ func checkProfileToken( } if data.Flags.Verbose { - text.Info(data.Output, "\n\nYour access token has now expired. We will attempt to refresh it") + text.Info(data.Output, "\nYour access token has now expired. We will attempt to refresh it") } updatedJWT, err := data.AuthServer.RefreshAccessToken(profileData.RefreshToken) From 01f5a7f5f1b5e5f43690945acf76c90c26af4257 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 15:23:24 +0000 Subject: [PATCH 112/115] fix(app): don't auto SSO if no profiles --- pkg/app/run.go | 303 ++++++++++++++++++----------------- pkg/commands/sso/root.go | 2 +- pkg/commands/sso/sso_test.go | 4 +- 3 files changed, 160 insertions(+), 149 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index f43afe850..7d52b4541 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -254,18 +254,20 @@ func Exec(data *global.Data) error { // The flags are only parsed/assigned via configureKingpin(). data.AuthServer.SetAPIEndpoint(apiEndpoint) - token, err := processToken(cmds, commandName, data) - if err != nil { - if errors.Is(err, fsterr.ErrDontContinue) { - return nil // we shouldn't exit 1 if user chooses to stop + if commandRequiresToken(commandName) { + token, err := processToken(cmds, commandName, data) + if err != nil { + if errors.Is(err, fsterr.ErrDontContinue) { + return nil // we shouldn't exit 1 if user chooses to stop + } + return fmt.Errorf("failed to process token: %w", err) } - return fmt.Errorf("failed to process token: %w", err) - } - data.APIClient, data.RTSClient, err = configureClients(token, apiEndpoint, data.APIClientFactory, data.Flags.Debug) - if err != nil { - data.ErrLog.Add(err) - return fmt.Errorf("error constructing client: %w", err) + data.APIClient, data.RTSClient, err = configureClients(token, apiEndpoint, data.APIClientFactory, data.Flags.Debug) + if err != nil { + data.ErrLog.Add(err) + return fmt.Errorf("error constructing client: %w", err) + } } f := checkForUpdates(data.Versioners.CLI, commandName, data.Flags.Quiet) @@ -334,22 +336,45 @@ func configureKingpin(data *global.Data) *kingpin.Application { // Finally, we check if there is a profile override in place (e.g. set via the // --profile flag or using the `profile` field in the fastly.toml manifest). func processToken(cmds []cmd.Command, commandName string, data *global.Data) (token string, err error) { - warningMessage := "No API token could be found" - var tokenSource lookup.Source + profileName, profileData, err := getProfile(data) + if err != nil { + return "", err + } + + outputMessage := "No API token could be found" + var ( + tokenSource lookup.Source + reauth bool + ) token, tokenSource = data.Token() // Check if token is from fastly.toml [profile] and refresh if expired. - tokenSource, warningMessage, err = checkProfileToken(tokenSource, commandName, warningMessage, data) - if err != nil { - return token, fmt.Errorf("failed to check profile token: %w", err) + // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. + if tokenSource == lookup.SourceFile { + tokenSource, outputMessage, reauth, err = checkProfileToken(profileData, profileName, outputMessage, tokenSource, data) + if err != nil { + return token, fmt.Errorf("failed to check profile token: %w", err) + } } // If there's no token available, and we need one for the invoked command, // then we'll trigger the SSO authentication flow. - if tokenSource == lookup.SourceUndefined && commandRequiresToken(commandName) { - token, tokenSource, err = ssoAuthentication(tokenSource, token, warningMessage, cmds, data) + if tokenSource == lookup.SourceUndefined { + // FIXME: Remove this conditional when SSO is GA. + // Once put back, it means "no token" == "automatic SSO". + // For now, a brand new CLI user will have to manually create long-lived + // tokens via the UI in order to use the Fastly CLI. + if data.Env.UseSSO != "1" && !data.Flags.SSO && !reauth { + return "", nil + } + err = ssoAuthentication(outputMessage, cmds, data) if err != nil { - return token, fmt.Errorf("failed to check profile token: %w", err) + return token, fmt.Errorf("failed to authenticate: %w", err) + } + // Recheck for token (should be persisted to the profile data now). + token, tokenSource = data.Token() + if tokenSource == lookup.SourceUndefined { + return token, fsterr.ErrNoToken } } @@ -363,138 +388,134 @@ func processToken(cmds []cmd.Command, commandName string, data *global.Data) (to return token, nil } -// checkProfileToken can potentially modify `tokenSource` to trigger a re-auth. -// It can also return a modified `warningMessage` depending on user case. -// -// NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. -func checkProfileToken( - tokenSource lookup.Source, - commandName, warningMessage string, - data *global.Data, -) (lookup.Source, string, error) { - if tokenSource == lookup.SourceFile && commandRequiresToken(commandName) { - var ( - profileData *config.Profile - found bool - name, profileName string - ) - switch { - case data.Flags.Profile != "": // --profile - profileName = data.Flags.Profile - case data.Manifest.File.Profile != "": // `profile` field in fastly.toml - profileName = data.Manifest.File.Profile - default: - profileName = "default" - } - for name, profileData = range data.Config.Profiles { - if (profileName == "default" && profileData.Default) || name == profileName { - // Once we find the default profile we can update the variable to be the - // associated profile name so later on we can use that information to - // update the specific profile. - if profileName == "default" { - profileName = name - } - found = true - break +// getProfile identifies the profile we should extract a token from. +func getProfile(data *global.Data) (string, *config.Profile, error) { + var ( + profileData *config.Profile + found bool + name, profileName string + ) + switch { + case data.Flags.Profile != "": // --profile + profileName = data.Flags.Profile + case data.Manifest.File.Profile != "": // `profile` field in fastly.toml + profileName = data.Manifest.File.Profile + default: + profileName = "default" + } + for name, profileData = range data.Config.Profiles { + if (profileName == "default" && profileData.Default) || name == profileName { + // Once we find the default profile we can update the variable to be the + // associated profile name so later on we can use that information to + // update the specific profile. + if profileName == "default" { + profileName = name } + found = true + break } - if !found { - return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) + } + if !found { + return "", nil, fmt.Errorf("failed to locate '%s' profile", profileName) + } + return profileName, profileData, nil +} + +// checkProfileToken can potentially modify `tokenSource` to trigger a re-auth. +// It can also return a modified `warningMessage` depending on user case. +func checkProfileToken(profileData *config.Profile, profileName, outputMessage string, tokenSource lookup.Source, data *global.Data) (lookup.Source, string, bool, error) { + // Allow user to opt-in to SSO/OAuth so they can replace their long-lived token. + if shouldSkipSSO(profileName, profileData, data) { + return tokenSource, outputMessage, false, nil + } + + // If OAuth flow has never been executed for the defined token, then we're + // dealing with a user with a pre-existing traditional token and they've + // opted into the OAuth flow. + if noSSOToken(profileData) { + outputMessage = "You've not authenticated via OAuth before" + tokenSource = forceReAuth() + return tokenSource, outputMessage, true, nil + } + + // Access Token has expired + if auth.TokenExpired(profileData.AccessTokenTTL, profileData.AccessTokenCreated) { + // Refresh Token has expired + if auth.TokenExpired(profileData.RefreshTokenTTL, profileData.RefreshTokenCreated) { + outputMessage = "Your access token has expired and so has your refresh token" + tokenSource = forceReAuth() + return tokenSource, outputMessage, true, nil } - // Allow user to opt-in to SSO/OAuth so they can replace their long-lived token. - if shouldSkipSSO(profileName, profileData, data) { - return tokenSource, warningMessage, nil + if data.Flags.Verbose { + text.Info(data.Output, "\nYour access token has now expired. We will attempt to refresh it") } - // If OAuth flow has never been executed for the defined token, then we're - // dealing with a user with a pre-existing traditional token and they've - // opted into the OAuth flow. - if noSSOToken(profileData) { - warningMessage = "You've not authenticated via OAuth before" - tokenSource = forceReAuth() - return tokenSource, warningMessage, nil + updatedJWT, err := data.AuthServer.RefreshAccessToken(profileData.RefreshToken) + if err != nil { + return tokenSource, outputMessage, false, fmt.Errorf("failed to refresh access token: %w", err) } - // Access Token has expired - if auth.TokenExpired(profileData.AccessTokenTTL, profileData.AccessTokenCreated) { - // Refresh Token has expired - if auth.TokenExpired(profileData.RefreshTokenTTL, profileData.RefreshTokenCreated) { - warningMessage = "Your access token has expired and so has your refresh token" - tokenSource = forceReAuth() - return tokenSource, warningMessage, nil - } + email, at, err := data.AuthServer.ValidateAndRetrieveAPIToken(updatedJWT.AccessToken) + if err != nil { + return tokenSource, outputMessage, false, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) + } + // NOTE: The refresh token can sometimes be refreshed along with the access token. + // This happens all the time in my testing but according to what is + // spec'd this apparently is something that _might_ happen. + // So after we get the refreshed access token, we check to see if the + // refresh token that was returned by the API call has also changed when + // compared to the refresh token stored in the CLI config file. + current := profile.Get(profileName, data.Config.Profiles) + if current == nil { + return tokenSource, outputMessage, false, fmt.Errorf("failed to locate '%s' profile", profileName) + } + now := time.Now().Unix() + refreshToken := current.RefreshToken + refreshTokenCreated := current.RefreshTokenCreated + refreshTokenTTL := current.RefreshTokenTTL + if current.RefreshToken != updatedJWT.RefreshToken { if data.Flags.Verbose { - text.Info(data.Output, "\nYour access token has now expired. We will attempt to refresh it") - } - - updatedJWT, err := data.AuthServer.RefreshAccessToken(profileData.RefreshToken) - if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to refresh access token: %w", err) - } - - email, at, err := data.AuthServer.ValidateAndRetrieveAPIToken(updatedJWT.AccessToken) - if err != nil { - return tokenSource, warningMessage, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) + text.Info(data.Output, "Your refresh token was also updated") + text.Break(data.Output) } + refreshToken = updatedJWT.RefreshToken + refreshTokenCreated = now + refreshTokenTTL = updatedJWT.RefreshExpiresIn + } - // NOTE: The refresh token can sometimes be refreshed along with the access token. - // This happens all the time in my testing but according to what is - // spec'd this apparently is something that _might_ happen. - // So after we get the refreshed access token, we check to see if the - // refresh token that was returned by the API call has also changed when - // compared to the refresh token stored in the CLI config file. - current := profile.Get(profileName, data.Config.Profiles) - if current == nil { - return tokenSource, warningMessage, fmt.Errorf("failed to locate '%s' profile", profileName) - } - now := time.Now().Unix() - refreshToken := current.RefreshToken - refreshTokenCreated := current.RefreshTokenCreated - refreshTokenTTL := current.RefreshTokenTTL - if current.RefreshToken != updatedJWT.RefreshToken { - if data.Flags.Verbose { - text.Info(data.Output, "Your refresh token was also updated") - text.Break(data.Output) - } - refreshToken = updatedJWT.RefreshToken - refreshTokenCreated = now - refreshTokenTTL = updatedJWT.RefreshExpiresIn - } - - ps, ok := profile.Edit(profileName, data.Config.Profiles, func(p *config.Profile) { - p.AccessToken = updatedJWT.AccessToken - p.AccessTokenCreated = now - p.AccessTokenTTL = updatedJWT.ExpiresIn - p.Email = email - p.RefreshToken = refreshToken - p.RefreshTokenCreated = refreshTokenCreated - p.RefreshTokenTTL = refreshTokenTTL - p.Token = at.AccessToken - }) - if !ok { - return tokenSource, warningMessage, fsterr.RemediationError{ - Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), - Remediation: "Run `fastly sso` to retry.", - } - } - data.Config.Profiles = ps - if err := data.Config.Write(data.ConfigPath); err != nil { - data.ErrLog.Add(err) - return tokenSource, warningMessage, fmt.Errorf("error saving config file: %w", err) + ps, ok := profile.Edit(profileName, data.Config.Profiles, func(p *config.Profile) { + p.AccessToken = updatedJWT.AccessToken + p.AccessTokenCreated = now + p.AccessTokenTTL = updatedJWT.ExpiresIn + p.Email = email + p.RefreshToken = refreshToken + p.RefreshTokenCreated = refreshTokenCreated + p.RefreshTokenTTL = refreshTokenTTL + p.Token = at.AccessToken + }) + if !ok { + return tokenSource, outputMessage, false, fsterr.RemediationError{ + Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), + Remediation: "Run `fastly sso` to retry.", } } + data.Config.Profiles = ps + if err := data.Config.Write(data.ConfigPath); err != nil { + data.ErrLog.Add(err) + return tokenSource, outputMessage, false, fmt.Errorf("error saving config file: %w", err) + } } - return tokenSource, warningMessage, nil + return tokenSource, outputMessage, false, nil } // shouldSkipSSO identifies if a config is a pre-v5 config and, if it is, // informs the user how they can use the SSO flow. It checks if the SSO // environment variable (or flag) has been set and enables the SSO flow if so. -func shouldSkipSSO(profileName string, pd *config.Profile, data *global.Data) bool { - if noSSOToken(pd) { +func shouldSkipSSO(profileName string, profileData *config.Profile, data *global.Data) bool { + if noSSOToken(profileData) { return data.Env.UseSSO != "1" && !data.Flags.SSO // FIXME: Put back messaging once SSO is GA. // if data.Env.UseSSO == "1" || data.Flags.SSO { @@ -522,12 +543,8 @@ func forceReAuth() lookup.Source { return lookup.SourceUndefined } -func ssoAuthentication( - tokenSource lookup.Source, - token, warningMessage string, - cmds []cmd.Command, - data *global.Data, -) (string, lookup.Source, error) { +// ssoAuthentication executes the `sso` command to handle authentication. +func ssoAuthentication(outputMessage string, cmds []cmd.Command, data *global.Data) error { for _, command := range cmds { commandName := strings.Split(command.Name(), " ")[0] if commandName == "sso" { @@ -535,34 +552,28 @@ func ssoAuthentication( if data.Verbose() { text.Break(data.Output) } - text.Important(data.Output, "%s. We need to open your browser to authenticate you.", warningMessage) + text.Important(data.Output, "%s. We need to open your browser to authenticate you.", outputMessage) text.Break(data.Output) cont, err := text.AskYesNo(data.Output, text.BoldYellow("Do you want to continue? [y/N]: "), data.Input) text.Break(data.Output) if err != nil { - return token, tokenSource, err + return err } if !cont { - return token, tokenSource, fsterr.ErrDontContinue + return fsterr.ErrDontContinue } } data.SkipAuthPrompt = true // skip the same prompt in `sso` command flow err := command.Exec(data.Input, data.Output) if err != nil { - return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) + return fmt.Errorf("failed to authenticate: %w", err) } text.Break(data.Output) break } } - - // Recheck for token (should be persisted to profile data). - token, tokenSource = data.Token() - if tokenSource == lookup.SourceUndefined { - return token, tokenSource, fsterr.ErrNoToken - } - return token, tokenSource, nil + return nil } func displayToken(tokenSource lookup.Source, data *global.Data) { diff --git a/pkg/commands/sso/root.go b/pkg/commands/sso/root.go index f8b7dbc9e..67ef0c499 100644 --- a/pkg/commands/sso/root.go +++ b/pkg/commands/sso/root.go @@ -39,7 +39,7 @@ type RootCommand struct { func NewRootCommand(parent cmd.Registerer, g *global.Data) *RootCommand { var c RootCommand c.Globals = g - // FIXME: Unhide this command once SSO authentication goes GA. + // FIXME: Unhide this command once SSO is GA. c.CmdClause = parent.Command("sso", "Single Sign-On authentication").Hidden() c.CmdClause.Arg("profile", "Profile to authenticate (i.e. create/update a token for)").Short('p').StringVar(&c.profile) return &c diff --git a/pkg/commands/sso/sso_test.go b/pkg/commands/sso/sso_test.go index 42329ae8a..2c848d91c 100644 --- a/pkg/commands/sso/sso_test.go +++ b/pkg/commands/sso/sso_test.go @@ -162,7 +162,7 @@ func TestSSO(t *testing.T) { // 7. Success processing `whoami` command. // We set an SSO token that has expired. // This allows us to validate the output message about expiration. - // We don't respond "Y" to prompt to reauthenticate. + // We don't respond "Y" to the prompt for reauthentication. // But we've mocked the request to succeed still so it doesn't matter. { TestScenario: testutil.TestScenario{ @@ -173,7 +173,7 @@ func TestSSO(t *testing.T) { ConfigFile: &config.File{ Profiles: config.Profiles{ "user": &config.Profile{ - AccessTokenCreated: time.Now().Add(-(time.Duration(300) * time.Second)).Unix(), // 5 mins ago + AccessTokenCreated: time.Now().Add(-(time.Duration(600) * time.Second)).Unix(), // 10 mins ago Default: true, Email: "test@example.com", Token: "mock-token", From 254e2a06fe77e8ca5a25926d885c18eba3aa4924 Mon Sep 17 00:00:00 2001 From: Integralist Date: Tue, 14 Nov 2023 18:13:16 +0000 Subject: [PATCH 113/115] refactor: auth flow --- pkg/app/run.go | 148 +++++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 79 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 7d52b4541..c8a40d4d0 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -255,7 +255,7 @@ func Exec(data *global.Data) error { data.AuthServer.SetAPIEndpoint(apiEndpoint) if commandRequiresToken(commandName) { - token, err := processToken(cmds, commandName, data) + token, tokenSource, err := processToken(cmds, data) if err != nil { if errors.Is(err, fsterr.ErrDontContinue) { return nil // we shouldn't exit 1 if user chooses to stop @@ -263,6 +263,13 @@ func Exec(data *global.Data) error { return fmt.Errorf("failed to process token: %w", err) } + if data.Verbose() { + displayToken(tokenSource, data) + } + if !data.Flags.Quiet { + checkConfigPermissions(commandName, tokenSource, data.Output) + } + data.APIClient, data.RTSClient, err = configureClients(token, apiEndpoint, data.APIClientFactory, data.Flags.Debug) if err != nil { data.ErrLog.Add(err) @@ -335,57 +342,56 @@ func configureKingpin(data *global.Data) *kingpin.Application { // // Finally, we check if there is a profile override in place (e.g. set via the // --profile flag or using the `profile` field in the fastly.toml manifest). -func processToken(cmds []cmd.Command, commandName string, data *global.Data) (token string, err error) { - profileName, profileData, err := getProfile(data) - if err != nil { - return "", err - } - - outputMessage := "No API token could be found" - var ( - tokenSource lookup.Source - reauth bool - ) +func processToken(cmds []cmd.Command, data *global.Data) (token string, tokenSource lookup.Source, err error) { token, tokenSource = data.Token() - // Check if token is from fastly.toml [profile] and refresh if expired. + // Check if token is from a profile. + // e.g. --profile, fastly.toml override, or config default profile. + // If it is, then we'll check if it is expired. + // // NOTE: tokens via FASTLY_API_TOKEN or --token aren't checked for a TTL. - if tokenSource == lookup.SourceFile { - tokenSource, outputMessage, reauth, err = checkProfileToken(profileData, profileName, outputMessage, tokenSource, data) + // This is because we don't have them persisted to disk. + // Meaning we can't check for a TTL or access an access/refresh token. + // So we have to presume those overrides are using a long-lived token. + switch tokenSource { + case lookup.SourceFile: + profileName, profileData, err := getProfile(data) if err != nil { - return token, fmt.Errorf("failed to check profile token: %w", err) + return "", tokenSource, err } - } - - // If there's no token available, and we need one for the invoked command, - // then we'll trigger the SSO authentication flow. - if tokenSource == lookup.SourceUndefined { + // User with long-lived token will skip SSO if they've not enabled it. + if shouldSkipSSO(profileName, profileData, data) { + return token, tokenSource, nil + } + // User now either has an existing SSO-based token or they want to migrate. + // If a long-lived token, then trigger SSO. + if longLivedToken(profileData) { + return ssoAuthentication("You've not authenticated via OAuth before", cmds, data) + } + // Otherwise, for an existing SSO token, check its freshness. + reauth, err := checkAndRefreshSSOToken(profileData, profileName, data) + if err != nil { + return token, tokenSource, fmt.Errorf("failed to check access/refresh token: %w", err) + } + if reauth { + return ssoAuthentication("Your access token has expired and so has your refresh token", cmds, data) + } + case lookup.SourceUndefined: + // If there's no token available, then trigger SSO authentication flow. + // // FIXME: Remove this conditional when SSO is GA. // Once put back, it means "no token" == "automatic SSO". // For now, a brand new CLI user will have to manually create long-lived // tokens via the UI in order to use the Fastly CLI. - if data.Env.UseSSO != "1" && !data.Flags.SSO && !reauth { - return "", nil + if data.Env.UseSSO != "1" && !data.Flags.SSO { + return "", tokenSource, nil } - err = ssoAuthentication(outputMessage, cmds, data) - if err != nil { - return token, fmt.Errorf("failed to authenticate: %w", err) - } - // Recheck for token (should be persisted to the profile data now). - token, tokenSource = data.Token() - if tokenSource == lookup.SourceUndefined { - return token, fsterr.ErrNoToken - } - } - - if data.Verbose() { - displayToken(tokenSource, data) - } - if !data.Flags.Quiet { - checkConfigPermissions(commandName, tokenSource, data.Output) + return ssoAuthentication("No API token could be found", cmds, data) + case lookup.SourceEnvironment, lookup.SourceFlag, lookup.SourceDefault: + // no-op } - return token, nil + return token, tokenSource, nil } // getProfile identifies the profile we should extract a token from. @@ -421,30 +427,13 @@ func getProfile(data *global.Data) (string, *config.Profile, error) { return profileName, profileData, nil } -// checkProfileToken can potentially modify `tokenSource` to trigger a re-auth. -// It can also return a modified `warningMessage` depending on user case. -func checkProfileToken(profileData *config.Profile, profileName, outputMessage string, tokenSource lookup.Source, data *global.Data) (lookup.Source, string, bool, error) { - // Allow user to opt-in to SSO/OAuth so they can replace their long-lived token. - if shouldSkipSSO(profileName, profileData, data) { - return tokenSource, outputMessage, false, nil - } - - // If OAuth flow has never been executed for the defined token, then we're - // dealing with a user with a pre-existing traditional token and they've - // opted into the OAuth flow. - if noSSOToken(profileData) { - outputMessage = "You've not authenticated via OAuth before" - tokenSource = forceReAuth() - return tokenSource, outputMessage, true, nil - } - +// checkAndRefreshSSOToken refreshes the access/refresh tokens if expired. +func checkAndRefreshSSOToken(profileData *config.Profile, profileName string, data *global.Data) (reauth bool, err error) { // Access Token has expired if auth.TokenExpired(profileData.AccessTokenTTL, profileData.AccessTokenCreated) { // Refresh Token has expired if auth.TokenExpired(profileData.RefreshTokenTTL, profileData.RefreshTokenCreated) { - outputMessage = "Your access token has expired and so has your refresh token" - tokenSource = forceReAuth() - return tokenSource, outputMessage, true, nil + return true, nil } if data.Flags.Verbose { @@ -453,12 +442,12 @@ func checkProfileToken(profileData *config.Profile, profileName, outputMessage s updatedJWT, err := data.AuthServer.RefreshAccessToken(profileData.RefreshToken) if err != nil { - return tokenSource, outputMessage, false, fmt.Errorf("failed to refresh access token: %w", err) + return false, fmt.Errorf("failed to refresh access token: %w", err) } email, at, err := data.AuthServer.ValidateAndRetrieveAPIToken(updatedJWT.AccessToken) if err != nil { - return tokenSource, outputMessage, false, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) + return false, fmt.Errorf("failed to validate JWT and retrieve API token: %w", err) } // NOTE: The refresh token can sometimes be refreshed along with the access token. @@ -469,7 +458,7 @@ func checkProfileToken(profileData *config.Profile, profileName, outputMessage s // compared to the refresh token stored in the CLI config file. current := profile.Get(profileName, data.Config.Profiles) if current == nil { - return tokenSource, outputMessage, false, fmt.Errorf("failed to locate '%s' profile", profileName) + return false, fmt.Errorf("failed to locate '%s' profile", profileName) } now := time.Now().Unix() refreshToken := current.RefreshToken @@ -496,7 +485,7 @@ func checkProfileToken(profileData *config.Profile, profileName, outputMessage s p.Token = at.AccessToken }) if !ok { - return tokenSource, outputMessage, false, fsterr.RemediationError{ + return false, fsterr.RemediationError{ Inner: fmt.Errorf("failed to update '%s' profile with new token data", profileName), Remediation: "Run `fastly sso` to retry.", } @@ -504,18 +493,19 @@ func checkProfileToken(profileData *config.Profile, profileName, outputMessage s data.Config.Profiles = ps if err := data.Config.Write(data.ConfigPath); err != nil { data.ErrLog.Add(err) - return tokenSource, outputMessage, false, fmt.Errorf("error saving config file: %w", err) + return false, fmt.Errorf("error saving config file: %w", err) } } - return tokenSource, outputMessage, false, nil + return false, nil } // shouldSkipSSO identifies if a config is a pre-v5 config and, if it is, // informs the user how they can use the SSO flow. It checks if the SSO // environment variable (or flag) has been set and enables the SSO flow if so. func shouldSkipSSO(profileName string, profileData *config.Profile, data *global.Data) bool { - if noSSOToken(profileData) { + if longLivedToken(profileData) { + // Skip SSO if user hasn't indicated they want to migrate. return data.Env.UseSSO != "1" && !data.Flags.SSO // FIXME: Put back messaging once SSO is GA. // if data.Env.UseSSO == "1" || data.Flags.SSO { @@ -532,19 +522,13 @@ func shouldSkipSSO(profileName string, profileData *config.Profile, data *global return false // don't skip SSO } -func noSSOToken(pd *config.Profile) bool { +func longLivedToken(pd *config.Profile) bool { // If user has followed SSO flow before, then these will not be zero values. return pd.AccessToken == "" && pd.RefreshToken == "" && pd.AccessTokenCreated == 0 && pd.RefreshTokenCreated == 0 } -// To re-authenticate we simply reset the tokenSource variable. -// A later conditional block catches it and trigger a re-auth. -func forceReAuth() lookup.Source { - return lookup.SourceUndefined -} - // ssoAuthentication executes the `sso` command to handle authentication. -func ssoAuthentication(outputMessage string, cmds []cmd.Command, data *global.Data) error { +func ssoAuthentication(outputMessage string, cmds []cmd.Command, data *global.Data) (token string, tokenSource lookup.Source, err error) { for _, command := range cmds { commandName := strings.Split(command.Name(), " ")[0] if commandName == "sso" { @@ -557,23 +541,29 @@ func ssoAuthentication(outputMessage string, cmds []cmd.Command, data *global.Da cont, err := text.AskYesNo(data.Output, text.BoldYellow("Do you want to continue? [y/N]: "), data.Input) text.Break(data.Output) if err != nil { - return err + return token, tokenSource, err } if !cont { - return fsterr.ErrDontContinue + return token, tokenSource, fsterr.ErrDontContinue } } data.SkipAuthPrompt = true // skip the same prompt in `sso` command flow err := command.Exec(data.Input, data.Output) if err != nil { - return fmt.Errorf("failed to authenticate: %w", err) + return token, tokenSource, fmt.Errorf("failed to authenticate: %w", err) } text.Break(data.Output) break } } - return nil + + // Updated token should be persisted to disk after command.Exec() completes. + token, tokenSource = data.Token() + if tokenSource == lookup.SourceUndefined { + return token, tokenSource, fsterr.ErrNoToken + } + return token, tokenSource, nil } func displayToken(tokenSource lookup.Source, data *global.Data) { From b2ad7813fe87b390abd1a4699931a9d0b933830b Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 15 Nov 2023 11:34:05 +0000 Subject: [PATCH 114/115] refactor: naming of variables --- pkg/app/run.go | 9 ++++----- pkg/cmd/cmd.go | 2 +- pkg/commands/whoami/whoami_test.go | 8 ++++---- pkg/config/config.go | 12 ++++++------ pkg/env/env.go | 28 +++++++++++++++------------- pkg/global/global.go | 24 ++++++++++++------------ 6 files changed, 42 insertions(+), 41 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index c8a40d4d0..012660ac1 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -107,7 +107,6 @@ var Init = func(args []string, stdin io.Reader) (*global.Data, error) { _ = md.File.Read(manifest.Filename) // Configure authentication inputs. - // We do this here so that we can mock the values in our test suite. req, err := http.NewRequest(http.MethodGet, auth.OIDCMetadata, nil) if err != nil { return nil, fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) @@ -311,13 +310,13 @@ func configureKingpin(data *global.Data) *kingpin.Application { // Interestingly, short flags can be reused but only across subcommands. tokenHelp := fmt.Sprintf("Fastly API token (or via %s)", env.APIToken) app.Flag("accept-defaults", "Accept default options for all interactive prompts apart from Yes/No confirmations").Short('d').BoolVar(&data.Flags.AcceptDefaults) - app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&data.Flags.Account) + app.Flag("account", "Fastly Accounts endpoint").Hidden().StringVar(&data.Flags.AccountEndpoint) + app.Flag("api", "Fastly API endpoint").Hidden().StringVar(&data.Flags.APIEndpoint) app.Flag("auto-yes", "Answer yes automatically to all Yes/No confirmations. This may suppress security warnings").Short('y').BoolVar(&data.Flags.AutoYes) // IMPORTANT: `--debug` is a built-in Kingpin flag so we must use `debug-mode`. app.Flag("debug-mode", "Print API request and response details (NOTE: can disrupt the normal CLI flow output formatting)").BoolVar(&data.Flags.Debug) // IMPORTANT: `--sso` causes a Kingpin runtime panic 🤦 so we use `enable-sso`. app.Flag("enable-sso", "Enable Single-Sign On (SSO) for current profile execution (see also: 'fastly sso')").Hidden().BoolVar(&data.Flags.SSO) - app.Flag("endpoint", "Fastly API endpoint").Hidden().StringVar(&data.Flags.Endpoint) app.Flag("non-interactive", "Do not prompt for user input - suitable for CI processes. Equivalent to --accept-defaults and --auto-yes").Short('i').BoolVar(&data.Flags.NonInteractive) app.Flag("profile", "Switch account profile for single command execution (see also: 'fastly profile switch')").Short('o').StringVar(&data.Flags.Profile) app.Flag("quiet", "Silence all output except direct command output. This won't prevent interactive prompts (see: --accept-defaults, --auto-yes, --non-interactive)").Short('q').BoolVar(&data.Flags.Quiet) @@ -602,9 +601,9 @@ func checkConfigPermissions(commandName string, tokenSource lookup.Source, out i func displayAPIEndpoint(endpoint string, endpointSource lookup.Source, out io.Writer) { switch endpointSource { case lookup.SourceFlag: - fmt.Fprintf(out, "Fastly API endpoint (via --endpoint): %s\n", endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via --api): %s\n", endpoint) case lookup.SourceEnvironment: - fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.Endpoint, endpoint) + fmt.Fprintf(out, "Fastly API endpoint (via %s): %s\n", env.APIEndpoint, endpoint) case lookup.SourceFile: fmt.Fprintf(out, "Fastly API endpoint (via config file): %s\n", endpoint) case lookup.SourceDefault, lookup.SourceUndefined: diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 48005f11f..2973e3dda 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -262,11 +262,11 @@ func IsGlobalFlagsOnly(args []string) bool { "--accept-defaults": 0, "-d": 0, "--account": 1, + "--api": 1, "--auto-yes": 0, "-y": 0, "--debug-mode": 0, "--enable-sso": 0, - "--endpoint": 1, "--help": 0, "--non-interactive": 0, "-i": 0, diff --git a/pkg/commands/whoami/whoami_test.go b/pkg/commands/whoami/whoami_test.go index 0ba6e8c94..9cedbb8c2 100644 --- a/pkg/commands/whoami/whoami_test.go +++ b/pkg/commands/whoami/whoami_test.go @@ -54,21 +54,21 @@ func TestWhoami(t *testing.T) { }, { name: "alternative endpoint from flag", - args: args("whoami --endpoint=https://staging.fastly.com -v"), + args: args("whoami --api=https://staging.fastly.com -v"), client: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, "Fastly API endpoint: https://api.fastly.com", - "Fastly API endpoint (via --endpoint): https://staging.fastly.com", + "Fastly API endpoint (via --api): https://staging.fastly.com", ), }, { name: "alternative endpoint from environment", args: args("whoami -v"), - env: config.Environment{Endpoint: "https://alternative.example.com"}, + env: config.Environment{APIEndpoint: "https://alternative.example.com"}, client: testutil.WhoamiVerifyClient(testutil.WhoamiBasicResponse), wantOutput: strings.ReplaceAll(basicOutputVerbose, "Fastly API endpoint: https://api.fastly.com", - fmt.Sprintf("Fastly API endpoint (via %s): https://alternative.example.com", env.Endpoint), + fmt.Sprintf("Fastly API endpoint (via %s): https://alternative.example.com", env.APIEndpoint), ), }, } { diff --git a/pkg/config/config.go b/pkg/config/config.go index f1654660c..7d67cbe02 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -442,14 +442,14 @@ func (f *File) Write(path string) error { // Environment represents all of the configuration parameters that can come // from environment variables. type Environment struct { - // Account is the env var we look in for the Accounts endpoint. - Account string + // AccountEndpoint is the env var we look in for the Accounts endpoint. + AccountEndpoint string + // APIEndpoint is the API endpoint to call. + APIEndpoint string // APIToken is the env var we look in for the Fastly API token. APIToken string // DebugMode indicates to the CLI it can display debug information. DebugMode string - // Endpoint is the API endpoint to call. - Endpoint string // UseSSO indicates if user wants to use SSO/OAuth token flow. // 1: enabled, 0: disabled. UseSSO string @@ -461,10 +461,10 @@ type Environment struct { // Read populates the fields from the provided environment. func (e *Environment) Read(state map[string]string) { + e.AccountEndpoint = state[env.AccountEndpoint] + e.APIEndpoint = state[env.APIEndpoint] e.APIToken = state[env.APIToken] - e.Account = state[env.Account] e.DebugMode = state[env.DebugMode] - e.Endpoint = state[env.Endpoint] e.UseSSO = state[env.UseSSO] e.WasmMetadataDisable = state[env.WasmMetadataDisable] } diff --git a/pkg/env/env.go b/pkg/env/env.go index b12398355..dcf3643bc 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -9,6 +9,14 @@ import ( ) const ( + // AccountEndpoint is the env var we look in for the Accounts endpoint. + // e.g. https://accounts.fastly.com + AccountEndpoint = "FASTLY_ACCOUNT_ENDPOINT" + + // APIEndpoint is the env var we look in for the API endpoint. + // e.g. https://api.fastly.com + APIEndpoint = "FASTLY_API_ENDPOINT" + // APIToken is the env var we look in for the Fastly API token. // gosec flagged this: // G101 (CWE-798): Potential hardcoded credentials @@ -16,27 +24,21 @@ const ( // #nosec APIToken = "FASTLY_API_TOKEN" - // UseSSO enables the CLI to validate the token as an OAuth token. - // These tokens aren't traditional tokens generated by the UI. - // Instead they generated via an OAuth flow (producing access/refresh tokens). - // Assigned value should be a boolean 1/0 (enable/disable). - UseSSO = "FASTLY_USE_SSO" + // CustomerID is the env var we look in for a Customer ID. + CustomerID = "FASTLY_CUSTOMER_ID" // DebugMode indicates to the CLI it can display debug information. // Set to "true" to enable debug mode. DebugMode = "FASTLY_DEBUG_MODE" - // Endpoint is the env var we look in for the API endpoint. - Endpoint = "FASTLY_API_ENDPOINT" - - // Account is the env var we look in for the Accounts endpoint. - Account = "FASTLY_ACCOUNT_ENDPOINT" - // ServiceID is the env var we look in for the required Service ID. ServiceID = "FASTLY_SERVICE_ID" - // CustomerID is the env var we look in for a Customer ID. - CustomerID = "FASTLY_CUSTOMER_ID" + // UseSSO enables the CLI to validate the token as an OAuth token. + // These tokens aren't traditional tokens generated by the UI. + // Instead they generated via an OAuth flow (producing access/refresh tokens). + // Assigned value should be a boolean 1/0 (enable/disable). + UseSSO = "FASTLY_USE_SSO" // WasmMetadataDisable is the env var we look in to disable all data // collection related to a Wasm binary. diff --git a/pkg/global/global.go b/pkg/global/global.go index 7a1eb973c..4918815a5 100644 --- a/pkg/global/global.go +++ b/pkg/global/global.go @@ -142,12 +142,12 @@ func (d *Data) Verbose() bool { // APIEndpoint yields the API endpoint. func (d *Data) APIEndpoint() (string, lookup.Source) { - if d.Flags.Endpoint != "" { - return d.Flags.Endpoint, lookup.SourceFlag + if d.Flags.APIEndpoint != "" { + return d.Flags.APIEndpoint, lookup.SourceFlag } - if d.Env.Endpoint != "" { - return d.Env.Endpoint, lookup.SourceEnvironment + if d.Env.APIEndpoint != "" { + return d.Env.APIEndpoint, lookup.SourceEnvironment } if d.Config.Fastly.APIEndpoint != DefaultAPIEndpoint && d.Config.Fastly.APIEndpoint != "" { @@ -159,12 +159,12 @@ func (d *Data) APIEndpoint() (string, lookup.Source) { // AccountEndpoint yields the Accounts endpoint. func (d *Data) AccountEndpoint() (string, lookup.Source) { - if d.Flags.Account != "" { - return d.Flags.Account, lookup.SourceFlag + if d.Flags.AccountEndpoint != "" { + return d.Flags.AccountEndpoint, lookup.SourceFlag } - if d.Env.Account != "" { - return d.Env.Account, lookup.SourceEnvironment + if d.Env.AccountEndpoint != "" { + return d.Env.AccountEndpoint, lookup.SourceEnvironment } if d.Config.Fastly.AccountEndpoint != DefaultAccountEndpoint && d.Config.Fastly.AccountEndpoint != "" { @@ -183,14 +183,14 @@ func (d *Data) AccountEndpoint() (string, lookup.Source) { type Flags struct { // AcceptDefaults auto-resolves prompts with a default defined. AcceptDefaults bool - // Account is the authentication host address. - Account string + // AccountEndpoint is the authentication host address. + AccountEndpoint string + // APIEndpoint is the Fastly API address. + APIEndpoint string // AutoYes auto-resolves Yes/No prompts by answering "Yes". AutoYes bool // Debug enables the CLI's debug mode. Debug bool - // Endpoint is the Fastly API address. - Endpoint string // NonInteractive auto-resolves all prompts. NonInteractive bool // Profile indicates the profile to use (consequently the 'token' used). From de1ebb1fa307932cdf98299a83111986480c5df5 Mon Sep 17 00:00:00 2001 From: Integralist Date: Wed, 15 Nov 2023 11:58:15 +0000 Subject: [PATCH 115/115] feat: support account endpoint override --- pkg/app/run.go | 17 ++++++++++++++++- pkg/auth/auth.go | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 012660ac1..16dda60e2 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -107,7 +107,8 @@ var Init = func(args []string, stdin io.Reader) (*global.Data, error) { _ = md.File.Read(manifest.Filename) // Configure authentication inputs. - req, err := http.NewRequest(http.MethodGet, auth.OIDCMetadata, nil) + metadataEndpoint := fmt.Sprintf(auth.OIDCMetadata, accountEndpoint(args, e)) + req, err := http.NewRequest(http.MethodGet, metadataEndpoint, nil) if err != nil { return nil, fmt.Errorf("failed to construct request object for OpenID Connect .well-known metadata: %w", err) } @@ -194,6 +195,20 @@ var Init = func(args []string, stdin io.Reader) (*global.Data, error) { }, nil } +// accountEndpoint parses the account endpoint from either the environment or +// the input arguments otherwise returns the default. +func accountEndpoint(args []string, e config.Environment) string { + if e.AccountEndpoint != "" { + return e.AccountEndpoint + } + for i, a := range args { + if a == "--account" && i+1 < len(args) { + return args[i+1] + } + } + return global.DefaultAccountEndpoint +} + // Exec constructs the application including all of the subcommands, parses the // args, invokes the client factory with the token to create a Fastly API // client, and executes the chosen command, using the provided io.Reader and diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 6528fad82..3f6cafd55 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -30,7 +30,7 @@ const RedirectURL = "http://localhost:8080/callback" // OIDCMetadata is OpenID Connect's metadata discovery mechanism. // https://swagger.io/docs/specification/authentication/openid-connect-discovery/ -const OIDCMetadata = "https://accounts.fastly.com/realms/fastly/.well-known/openid-configuration" +const OIDCMetadata = "%s/realms/fastly/.well-known/openid-configuration" // WellKnownEndpoints represents the OpenID Connect metadata. type WellKnownEndpoints struct {