Skip to content

Commit

Permalink
Fabricating configs (for now, subnets only) (#299)
Browse files Browse the repository at this point in the history
Signed-off-by: Ziv Nevo <nevo@il.ibm.com>
  • Loading branch information
zivnevo authored Jul 29, 2024
1 parent 3230451 commit 2b96e4e
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 1 deletion.
25 changes: 24 additions & 1 deletion cmd/collect/subcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ var (
regions []string
resourceGroupID string
outputFile string

fabricatesOpts common.FabricateOptions
)

func newRootCommand() *cobra.Command {
Expand All @@ -39,8 +41,11 @@ func newRootCommand() *cobra.Command {
rootCmd.PersistentFlags().VarP(&provider, providerFlag, "p", "collect resources from an account in this cloud provider")
_ = rootCmd.MarkPersistentFlagRequired(providerFlag)

rootCmd.PersistentFlags().StringVar(&outputFile, "out", "", "file path to store results")

rootCmd.AddCommand(newCollectCommand())
rootCmd.AddCommand(newGetRegionsCommand())
rootCmd.AddCommand(newFabricateCommand())

rootCmd.SetHelpCommand(&cobra.Command{Hidden: true}) // disable help command. should use --help flag instead

Expand All @@ -58,7 +63,6 @@ func newCollectCommand() *cobra.Command {

collectCmd.Flags().StringArrayVarP(&regions, "region", "r", nil, "cloud region from which to collect resources")
collectCmd.Flags().StringVar(&resourceGroupID, "resource-group", "", "resource group id or name from which to collect resources")
collectCmd.Flags().StringVar(&outputFile, "out", "", "file path to store results")

return collectCmd
}
Expand All @@ -80,3 +84,22 @@ func newGetRegionsCommand() *cobra.Command {
},
}
}

func newFabricateCommand() *cobra.Command {
fabricateCmd := &cobra.Command{
Use: "fabricate",
Short: "Fabricate synthetic data",
Long: `Generates synthetic data with a given number of VPCs, Subnets, VSIs, ...`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
resources := factory.GetResourceContainer(provider, regions, "")
resources.Fabricate(&fabricatesOpts)
OutputResources(resources, outputFile)
return nil
},
}
fabricateCmd.Flags().IntVar(&fabricatesOpts.NumVPCs, "num-vpcs", 1, "Number of VPCs to generate")
fabricateCmd.Flags().IntVar(&fabricatesOpts.SubnetsPerVPC, "subnets-per-vpc", 1, "Number of subnets to generate in each VPC")

return fabricateCmd
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/IBM/vpc-go-sdk v0.54.0
github.com/aws/aws-sdk-go-v2/config v1.27.21
github.com/aws/aws-sdk-go-v2/service/ec2 v1.163.0
github.com/np-guard/models v0.3.4
github.com/spf13/cobra v1.8.1
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/np-guard/models v0.3.4 h1:HOhVi6wyGvo+KmYBnQ5Km5HYCF+/PQlDs1v7mL1v05g=
github.com/np-guard/models v0.3.4/go.mod h1:mqE2Irf8r+7HWh8fII0fWbWyQRMHGEo2SgSLN/6VKs8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
Expand Down
3 changes: 3 additions & 0 deletions pkg/aws/resources_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func (resources *ResourcesContainer) GetResources() common.ResourcesModel {
return resources
}

func (resources *ResourcesContainer) Fabricate(opts *common.FabricateOptions) { // TODO: implement
}

// CollectResourcesFromAPI uses AWS APIs to collect resource configuration information
func (resources *ResourcesContainer) CollectResourcesFromAPI() error { //nolint:gocyclo // due to many API calls
// Load the Shared AWS Configuration (~/.aws/config)
Expand Down
6 changes: 6 additions & 0 deletions pkg/common/resources_container_inf.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type ResourcesContainerInf interface {
ToJSONString() (string, error)
AllRegions() []string
GetResources() ResourcesModel
Fabricate(opts *FabricateOptions)
}

type ResourcesModel interface {
Expand All @@ -22,3 +23,8 @@ type ResourceModelMetadata struct {
Version string `json:"collector_version"`
Provider string `json:"provider"`
}

type FabricateOptions struct {
NumVPCs int
SubnetsPerVPC int
}
158 changes: 158 additions & 0 deletions pkg/ibm/fabricate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
Copyright 2023- IBM Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package ibm

import (
"fmt"
"math/rand"

"github.com/IBM/vpc-go-sdk/vpcv1"

"github.com/np-guard/cloud-resource-collector/pkg/common"
"github.com/np-guard/cloud-resource-collector/pkg/ibm/datamodel"
"github.com/np-guard/models/pkg/ipblock"
)

const (
ipElementSize = 256
defaultCidrPrefix = 24
maxNumACLsInVPC = 10
maxNumRulesInNACL = 10
)

var (
regionsAndZones = map[string][]string{
"us-south": {"us-south1", "us-south2", "us-south3"},
"us-east": {"us-east1", "us-east2", "us-east3"},
}
uid = map[string]int{}
availableIPs, _ = ipblock.FromCidrList([]string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"})
nACLsOfVPC = map[string][]*datamodel.NetworkACL{}

vpcType = vpcv1.VPCReferenceResourceTypeVPCConst
ipv4 = vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAllIPVersionIpv4Const
allProtocols = vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAllProtocolAllConst
)

func getUID(resource string) *string {
res := fmt.Sprintf("%s-%d", resource, uid[resource])
uid[resource]++
return &res
}

func getVPCRef(vpcID *string) *vpcv1.VPCReference {
return &vpcv1.VPCReference{CRN: vpcID, ID: vpcID, Name: vpcID, ResourceType: &vpcType}
}

func chooseRandElem[T any](pool []T) *T {
return &pool[rand.Intn(len(pool))] //nolint:gosec // weak random is ok here
}

func getRandomRegion() string {
regionNum := rand.Intn(len(regionsAndZones)) //nolint:gosec // weak random is ok here
i := 0
for k := range regionsAndZones {
if i == regionNum {
return k
}
i++
}
return ""
}

func getAvailableInternalCidrBlock() *string {
prefix := defaultCidrPrefix - rand.Intn(2) //nolint:gosec // weak random is ok here
baseIP := availableIPs.FirstIPAddress()
cidr := fmt.Sprintf("%s/%d", baseIP, prefix)
cidrIPBlock, _ := ipblock.FromCidr(cidr)
availableIPs = availableIPs.Subtract(cidrIPBlock)
return &cidr
}

func getRandomCidr() *string {
var ipElem [4]int
for i := 0; i < len(ipElem); i++ {
ipElem[i] = rand.Intn(ipElementSize) //nolint:gosec // weak random is ok here
}
prefix := rand.Intn(2) //nolint:gosec // weak random is ok here
cidr := fmt.Sprintf("%d.%d.%d.%d/%d", ipElem[0], ipElem[1], ipElem[2], ipElem[3], prefix)
return &cidr
}

func makeNACLRules() []vpcv1.NetworkACLRuleItemIntf {
res := []vpcv1.NetworkACLRuleItemIntf{}

numRules := rand.Intn(maxNumRulesInNACL) //nolint:gosec // weak random is ok here
for i := 0; i < numRules; i++ {
ruleID := getUID("aclRule")
rule := vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAll{
ID: ruleID,
Name: ruleID,
Action: chooseRandElem([]string{
vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAllActionAllowConst,
vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAllActionDenyConst}),
Direction: chooseRandElem([]string{
vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAllDirectionInboundConst,
vpcv1.NetworkACLRuleItemNetworkACLRuleProtocolAllDirectionOutboundConst,
}),
Source: getRandomCidr(),
Destination: getRandomCidr(),
Protocol: &allProtocols,
IPVersion: &ipv4,
}
res = append(res, &rule)
}

return res
}

func makeNACLs(vpcID string) []*datamodel.NetworkACL {
numNacls := rand.Intn(maxNumACLsInVPC) + 1 //nolint:gosec // weak random is ok here
res := []*datamodel.NetworkACL{}
for i := 0; i < numNacls; i++ {
naclID := getUID("nacl")
sdkNACL := vpcv1.NetworkACL{ID: naclID, CRN: naclID, Name: naclID, VPC: getVPCRef(&vpcID)}
sdkNACL.Rules = makeNACLRules()
modelNacl := datamodel.NewNetworkACL(&sdkNACL)
res = append(res, modelNacl)
nACLsOfVPC[vpcID] = append(nACLsOfVPC[vpcID], modelNacl)
}

return res
}

func getNACLRef(nacl *datamodel.NetworkACL) *vpcv1.NetworkACLReference {
return &vpcv1.NetworkACLReference{CRN: nacl.CRN, ID: nacl.ID, Name: nacl.Name}
}

func getSubnetRef(subnet *datamodel.Subnet) vpcv1.SubnetReference {
return vpcv1.SubnetReference{CRN: subnet.CRN, ID: subnet.ID, Name: subnet.Name}
}

func (resources *ResourcesContainer) Fabricate(opts *common.FabricateOptions) {
for i := 0; i < opts.NumVPCs; i++ {
vpcID := getUID("vpc")
vpcRegion := getRandomRegion()
sdkVPC := vpcv1.VPC{ID: vpcID, Name: vpcID, CRN: vpcID}
vpc := datamodel.NewVPC(&sdkVPC, vpcRegion, nil)
resources.VpcList = append(resources.VpcList, vpc)

resources.NetworkACLList = append(resources.NetworkACLList, makeNACLs(*vpcID)...)

zone := vpcv1.ZoneReference{Name: chooseRandElem(regionsAndZones[vpcRegion])}
for s := 0; s < opts.SubnetsPerVPC; s++ {
subnetID := getUID("subnet")
sdkSubnet := vpcv1.Subnet{ID: subnetID, Name: subnetID, CRN: subnetID, VPC: getVPCRef(vpcID), Zone: &zone}
sdkSubnet.Ipv4CIDRBlock = getAvailableInternalCidrBlock()
subnetNACL := *chooseRandElem(nACLsOfVPC[*vpcID])
sdkSubnet.NetworkACL = getNACLRef(subnetNACL)
subnet := datamodel.NewSubnet(&sdkSubnet, nil)
subnetNACL.Subnets = append(subnetNACL.Subnets, getSubnetRef(subnet))
resources.SubnetList = append(resources.SubnetList, subnet)
}
}
}

0 comments on commit 2b96e4e

Please sign in to comment.