Skip to content

Commit

Permalink
new output formats (html, excel) and refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
cmendible committed Jan 30, 2023
1 parent b0cd7c2 commit f4f94f6
Show file tree
Hide file tree
Showing 40 changed files with 524 additions and 365 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
run: |
go install gotest.tools/gotestsum@latest
CGO_ENABLED=0 gotestsum --jsonfile ./test_report_unit.json --format standard-quiet -- ./internal/... $COVERAGE_OPTS --tags=unit
go test ./internal/analyzers/
go test ./internal/scanners/
- name: Codecov
if: matrix.target_os == 'linux'
Expand Down
171 changes: 43 additions & 128 deletions cmd/azqr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ import (
"fmt"
"log"
"os"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/cmendible/azqr/internal/analyzers"
"github.com/cmendible/azqr/internal/report/templates"
"github.com/fbiville/markdown-table-formatter/pkg/markdown"
"github.com/cmendible/azqr/internal/scanners"
"github.com/cmendible/azqr/internal/renderers"
"golang.org/x/sync/semaphore"
)

Expand All @@ -29,8 +27,8 @@ var (
func main() {
subscriptionPtr := flag.String("s", "", "Azure Subscription Id (Required)")
resourceGroupPtr := flag.String("g", "", "Azure Resource Group")
outputPtr := flag.String("o", "report.md", "Output file")
customerPtr := flag.String("c", "<Replace with Customer Name>", "Customer Name")
outputPtr := flag.String("o", "azqr_report", "Output file prefix")
customerPtr := flag.String("c", "Replace_With_Customer_Name", "Customer Name")
detail := flag.Bool("d", false, "Enable more details in the report")
concurrency := flag.Int("p", defaultConcurrency, fmt.Sprintf("Parallel processes. Default to %d. A < 0 value will use the maxmimum concurrency.", defaultConcurrency))
ver := flag.Bool("v", false, "Print version and exit")
Expand All @@ -39,9 +37,16 @@ func main() {

subscriptionID := *subscriptionPtr
resourceGroupName := *resourceGroupPtr
outputFile := *outputPtr
outputFilePrefix := *outputPtr
customer := *customerPtr

current_time := time.Now()
outputFileStamp := fmt.Sprintf("%d_%02d_%02d_T%02d%02d%02d",
current_time.Year(), current_time.Month(), current_time.Day(),
current_time.Hour(), current_time.Minute(), current_time.Second())

outputFile := fmt.Sprintf("%s_%s", outputFilePrefix, outputFileStamp)

if *ver {
fmt.Printf("azqr version: %s", version)
os.Exit(0)
Expand Down Expand Up @@ -79,112 +84,77 @@ func main() {
}
}

svcanalyzers := []analyzers.IAzureServiceAnalyzer{
&analyzers.AKSAnalyzer{},
&analyzers.APIManagementAnalyzer{},
&analyzers.ApplicationGatewayAnalyzer{},
&analyzers.ContainerAppsAnalyzer{},
&analyzers.ContainerInstanceAnalyzer{},
&analyzers.CosmosDBAnalyzer{},
&analyzers.ContainerRegistryAnalyzer{},
&analyzers.EventHubAnalyzer{},
&analyzers.EventGridAnalyzer{},
&analyzers.KeyVaultAnalyzer{},
&analyzers.AppServiceAnalyzer{},
&analyzers.RedisAnalyzer{},
&analyzers.ServiceBusAnalyzer{},
&analyzers.SignalRAnalyzer{},
&analyzers.StorageAnalyzer{},
&analyzers.PostgreAnalyzer{},
&analyzers.SQLAnalyzer{},
svcScanners := []scanners.IAzureScanner{
&scanners.AKSScanner{},
&scanners.APIManagementScanner{},
&scanners.ApplicationGatewayScanner{},
&scanners.ContainerAppsScanner{},
&scanners.ContainerInstanceScanner{},
&scanners.CosmosDBScanner{},
&scanners.ContainerRegistryScanner{},
&scanners.EventHubScanner{},
&scanners.EventGridScanner{},
&scanners.KeyVaultScanner{},
&scanners.AppServiceScanner{},
&scanners.RedisScanner{},
&scanners.ServiceBusScanner{},
&scanners.SignalRScanner{},
&scanners.StorageScanner{},
&scanners.PostgreScanner{},
&scanners.SQLScanner{},
}

ctx, cancel := context.WithCancel(ctx)
defer cancel()

config := analyzers.ServiceAnalizerConfig{
config := scanners.ScannerConfig{
Ctx: ctx,
SubscriptionID: subscriptionID,
Cred: cred,
EnableDetailedScan: *detail,
}

for _, a := range svcanalyzers {
for _, a := range svcScanners {
err := a.Init(config)
if err != nil {
log.Fatal(err)
}
}

var all []analyzers.IAzureServiceResult
var all []scanners.IAzureServiceResult
rc := ReviewContext{
Ctx: ctx,
ResCh: make(chan []analyzers.IAzureServiceResult),
ResCh: make(chan []scanners.IAzureServiceResult),
ErrCh: make(chan error),
}
for _, r := range resourceGroups {
log.Printf("Analyzing Resource Group %s", r)
go reviewRunner(&rc, r, &svcanalyzers, *concurrency)
res, err := waitForReviews(&rc, len(svcanalyzers))
go reviewRunner(&rc, r, &svcScanners, *concurrency)
res, err := waitForReviews(&rc, len(svcScanners))
// As soon as any error happen, we cancel every still running analysis
if err != nil {
cancel()
log.Fatal(err)
}
all = append(all, *res...)
}
resultsTable := renderTable(all)

var allFunctions []analyzers.IAzureServiceResult
for _, r := range all {
v, ok := r.(analyzers.AzureFunctionAppResult)
if ok {
allFunctions = append(allFunctions, v)
}
}

reportTemplate := templates.GetTemplates("Report.md")
reportTemplate = strings.Replace(reportTemplate, "{{results}}", resultsTable, 1)
reportTemplate = strings.Replace(reportTemplate, "{{date}}", time.Now().Format("2006-01-02"), 1)
reportTemplate = strings.Replace(reportTemplate, "{{customer}}", customer, -1)

recommendations := ""
dict := map[string]bool{}
for _, r := range all {
parsedType := strings.Replace(r.GetResourceType(), "/", ".", -1)
if _, ok := dict[r.GetResourceType()]; !ok {
dict[r.GetResourceType()] = true
recommendations += "\n\n"
recommendations += templates.GetTemplates(fmt.Sprintf("%s.md", parsedType))

if r.GetResourceType() == "Microsoft.Web/serverfarms/sites" && len(allFunctions) > 0 && config.EnableDetailedScan {
recommendations = strings.Replace(recommendations, "{{functions}}", renderDetailsTable(allFunctions), 1)
} else {
recommendations = strings.Replace(recommendations, "{{functions}}", "", 1)
}
}
}

reportTemplate = strings.Replace(reportTemplate, "{{recommendations}}", recommendations, 1)

err = os.WriteFile(outputFile, []byte(reportTemplate), 0644)
if err != nil {
log.Fatal(err)
}
renderers.CreateMarkdownReport(all, customer, outputFile, config.EnableDetailedScan)
renderers.CreateExcelReport(all, outputFile)
}

// ReviewContext A running resource group analysis support context
type ReviewContext struct {
// Review context, will be passed to every created goroutines
Ctx context.Context
// Communication interface for each review results
ResCh chan []analyzers.IAzureServiceResult
ResCh chan []scanners.IAzureServiceResult
// Communication interface for errors
ErrCh chan error
}

// Run a review on a peculiar resource group "r" with the appropriates analysers using "concurrency" goroutines
func reviewRunner(rc *ReviewContext, r string, svcAnalysers *[]analyzers.IAzureServiceAnalyzer, concurrency int) {
func reviewRunner(rc *ReviewContext, r string, svcAnalysers *[]scanners.IAzureScanner, concurrency int) {
if concurrency <= 0 {
concurrency = len(*svcAnalysers)
}
Expand All @@ -198,7 +168,7 @@ func reviewRunner(rc *ReviewContext, r string, svcAnalysers *[]analyzers.IAzureS
// the iteration variable, as only the last element of the loop will
// be processed
analyserPtr := &(*svcAnalysers)[i]
go func(a *analyzers.IAzureServiceAnalyzer, r string) {
go func(a *scanners.IAzureScanner, r string) {
defer sem.Release(1)
// In case the analysis was cancelled, we don't need to execute the review
if context.Canceled == rc.Ctx.Err() {
Expand All @@ -214,9 +184,9 @@ func reviewRunner(rc *ReviewContext, r string, svcAnalysers *[]analyzers.IAzureS
}

// Wait for at least "nb" goroutines to hands their result and return them
func waitForReviews(rc *ReviewContext, nb int) (*[]analyzers.IAzureServiceResult, error) {
func waitForReviews(rc *ReviewContext, nb int) (*[]scanners.IAzureServiceResult, error) {
received := 0
reviews := make([]analyzers.IAzureServiceResult, 0)
reviews := make([]scanners.IAzureServiceResult, 0)
for {
select {
// In case a timeout is set
Expand Down Expand Up @@ -265,58 +235,3 @@ func listResourceGroup(ctx context.Context, subscriptionID string, cred azcore.T
}
return resourceGroups, nil
}

func renderTable(results []analyzers.IAzureServiceResult) string {
if len(results) == 0 {
return "No results found."
}

heathers := results[0].GetProperties()

rows := [][]string{}
for _, r := range results {
rows = append(mapToRow(heathers, r.ToMap()), rows...)
}

prettyPrintedTable, err := markdown.NewTableFormatterBuilder().
WithPrettyPrint().
Build(heathers...).
Format(rows)

if err != nil {
log.Fatal(err)
}
fmt.Println("")
fmt.Println(prettyPrintedTable)
return prettyPrintedTable
}

func renderDetailsTable(results []analyzers.IAzureServiceResult) string {
heathers := results[0].GetDetailProperties()

rows := [][]string{}
for _, r := range results {
rows = append(mapToRow(heathers, r.ToDetail()), rows...)
}

prettyPrintedTable, err := markdown.NewTableFormatterBuilder().
WithPrettyPrint().
Build(heathers...).
Format(rows)

if err != nil {
log.Fatal(err)
}

return prettyPrintedTable
}

func mapToRow(heathers []string, m map[string]string) [][]string {
v := make([]string, 0, len(m))

for _, k := range heathers {
v = append(v, m[k])
}

return [][]string{v}
}
15 changes: 11 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.1.0
github.com/Azure/go-autorest/autorest/to v0.4.0
github.com/fbiville/markdown-table-formatter v0.3.0
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c
github.com/xuri/excelize/v2 v2.7.0
golang.org/x/sync v0.1.0
)

Expand All @@ -37,9 +39,14 @@ require (
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
github.com/google/uuid v1.1.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/text v0.3.7 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
)
69 changes: 60 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,32 +51,83 @@ github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+X
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE=
github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
github.com/fbiville/markdown-table-formatter v0.3.0 h1:PIm1UNgJrFs8q1htGTw+wnnNYvwXQMMMIKNZop2SSho=
github.com/fbiville/markdown-table-formatter v0.3.0/go.mod h1:q89TDtSEVDdTaufgSbfHpNVdPU/bmfvqNkrC5HagmLY=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c h1:iyaGYbCmcYK0Ja9a3OUa2Fo+EaN0cbLu0eKpBwPFzc8=
github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw=
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.7.0 h1:Hri/czwyRCW6f6zrCDWXcXKshlq4xAZNpNOpdfnFhEw=
github.com/xuri/excelize/v2 v2.7.0/go.mod h1:ebKlRoS+rGyLMyUx3ErBECXs/HNYqyj+PbkkKRK5vSI=
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
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.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit f4f94f6

Please sign in to comment.