cli/scorecard/violations.go (147 lines of code) (raw):

// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package scorecard import ( "bufio" "context" "fmt" "io" "os" "path/filepath" "sync" "cloud.google.com/go/storage" "github.com/GoogleCloudPlatform/config-validator/pkg/api/validator" cvasset "github.com/GoogleCloudPlatform/config-validator/pkg/asset" "github.com/gammazero/workerpool" "github.com/pkg/errors" ) func getDataFromReader(reader io.Reader) ([]*validator.Asset, error) { const maxCapacity = 1024 * 1024 scanner := bufio.NewScanner(reader) buf := make([]byte, maxCapacity) scanner.Buffer(buf, maxCapacity) var pbAssets []*validator.Asset for scanner.Scan() { pbAsset, err := getAssetFromJSON(scanner.Bytes()) if err != nil { return nil, err } pbAssets = append(pbAssets, pbAsset) } return pbAssets, nil } func getDataFromBucket(bucketName string) ([]*validator.Asset, error) { ctx := context.Background() client, err := storage.NewClient(ctx) if err != nil { return nil, err } bucket := client.Bucket(bucketName) var pbAssets []*validator.Asset for _, objectName := range destinationObjectNames { reader, err := bucket.Object(objectName).NewReader(ctx) if err != nil { fmt.Println("WARNING: Unable to read inventory file :", objectName, err) continue } defer func() { if err := reader.Close(); err != nil { panic(err) } }() assets, err := getDataFromReader(reader) if err != nil { return nil, err } pbAssets = append(pbAssets, assets...) } if len(pbAssets) == 0 { return nil, fmt.Errorf("no inventory found") } return pbAssets, nil } func getDataFromFile(caiDirName string) ([]*validator.Asset, error) { var pbAssets []*validator.Asset for _, objectName := range destinationObjectNames { reader, err := os.Open(filepath.Join(caiDirName, objectName)) if err != nil { fmt.Println("WARNING: Unable to read inventory file :", objectName, err) continue } defer func() { if err := reader.Close(); err != nil { panic(err) } }() assets, err := getDataFromReader(reader) if err != nil { return nil, err } pbAssets = append(pbAssets, assets...) } if len(pbAssets) == 0 { return nil, fmt.Errorf("no inventory found") } return pbAssets, nil } func getDataFromStdin() ([]*validator.Asset, error) { return getDataFromReader(os.Stdin) } // getViolations finds all Config Validator violations for a given Inventory func getViolations(inventory *InventoryConfig, config *ScoringConfig) ([]*RichViolation, error) { var err error var pbAssets []*validator.Asset if inventory.bucketName != "" { pbAssets, err = getDataFromBucket(inventory.bucketName) if err != nil { return nil, errors.Wrap(err, "Fetching inventory from Bucket") } } else if inventory.dirPath != "" { pbAssets, err = getDataFromFile(inventory.dirPath) if err != nil { return nil, errors.Wrap(err, "Fetching inventory from local directory") } } else if inventory.readFromStdin { pbAssets, err = getDataFromStdin() if err != nil { return nil, errors.Wrap(err, "Reading from stdin") } } richViolations := make([]*RichViolation, 0) wp := workerpool.New(inventory.workers) var badAsset *validator.Asset var mu sync.Mutex for _, asset := range pbAssets { asset := asset wp.Submit(func() { violations, errAsset := config.validator.ReviewAsset(context.Background(), asset) if errAsset != nil { err = errAsset badAsset = asset wp.Stop() } for _, violation := range violations { richViolation := RichViolation{violation, "", violation.Resource, violation.Message, violation.Metadata, asset} mu.Lock() richViolations = append(richViolations, &richViolation) mu.Unlock() } }) } wp.StopWait() if err != nil { return nil, errors.Wrapf(err, "reviewing asset %s", badAsset) } else { return richViolations, nil } } // converts raw JSON into Asset proto func getAssetFromJSON(input []byte) (*validator.Asset, error) { pbAsset := &validator.Asset{} err := unMarshallAsset(input, pbAsset) if err != nil { return nil, errors.Wrapf(err, "converting asset %s to proto", string(input)) } err = cvasset.SanitizeAncestryPath(pbAsset) if err != nil { return nil, errors.Wrapf(err, "fetching ancestry path for %s", pbAsset) } Log.Debug("Asset converted", "name", pbAsset.GetName(), "ancestry", pbAsset.GetAncestryPath()) return pbAsset, nil }