From 736618501b23d93c09abe9cc9ae0fc1715a6db45 Mon Sep 17 00:00:00 2001 From: Issif Date: Mon, 2 Dec 2019 20:54:54 +0100 Subject: [PATCH] first public commit --- LICENSE | 191 +++++++++++ Makefile | 10 + README.md | 360 ++++++++++++++++++++ addresses.go | 100 ++++++ autoscalinggroups.go | 134 ++++++++ availibiltyzones.go | 65 ++++ cacheclusters.go | 116 +++++++ cachesubnetgroups.go | 91 +++++ connector.go | 62 ++++ dbclusterparametergroups.go | 72 ++++ dbclusters.go | 144 ++++++++ dbinstances.go | 154 +++++++++ dbparametergroups.go | 72 ++++ dbsubnetgroups.go | 94 +++++ dgraph.go | 115 +++++++ docker-compose.yml | 11 + go.mod | 9 + go.sum | 75 ++++ images.go | 104 ++++++ img/schemas.png | Bin 0 -> 113078 bytes img/screenshot.png | Bin 0 -> 121200 bytes instanceprofiles.go | 72 ++++ instances.go | 133 ++++++++ keypairs.go | 66 ++++ launchconfigurations.go | 107 ++++++ launchtemplates.go | 74 ++++ loadbalancers.go | 199 +++++++++++ main.go | 156 +++++++++ natgateways.go | 98 ++++++ optionsgroups.go | 76 +++++ schema.go | 661 ++++++++++++++++++++++++++++++++++++ securitygroups.go | 194 +++++++++++ snapshots.go | 105 ++++++ subnets.go | 104 ++++++ targetgroups.go | 109 ++++++ volumes.go | 108 ++++++ vpcpeeringconnections.go | 94 +++++ vpcs.go | 72 ++++ 38 files changed, 4407 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 addresses.go create mode 100644 autoscalinggroups.go create mode 100644 availibiltyzones.go create mode 100644 cacheclusters.go create mode 100644 cachesubnetgroups.go create mode 100644 connector.go create mode 100644 dbclusterparametergroups.go create mode 100644 dbclusters.go create mode 100644 dbinstances.go create mode 100644 dbparametergroups.go create mode 100644 dbsubnetgroups.go create mode 100644 dgraph.go create mode 100644 docker-compose.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 images.go create mode 100644 img/schemas.png create mode 100644 img/screenshot.png create mode 100644 instanceprofiles.go create mode 100644 instances.go create mode 100644 keypairs.go create mode 100644 launchconfigurations.go create mode 100644 launchtemplates.go create mode 100644 loadbalancers.go create mode 100644 main.go create mode 100644 natgateways.go create mode 100644 optionsgroups.go create mode 100644 schema.go create mode 100644 securitygroups.go create mode 100644 snapshots.go create mode 100644 subnets.go create mode 100644 targetgroups.go create mode 100644 volumes.go create mode 100644 vpcpeeringconnections.go create mode 100644 vpcs.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aa78757 --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2018 Docker, Inc. + + 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 + + https://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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..37bc022 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +#!make + +up: + docker-compose -f docker-compose.yml up -d + +stop: + docker-compose -f docker-compose.yml stop + +rm: + docker-compose -f docker-compose.yml rm \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9fa84c6 --- /dev/null +++ b/README.md @@ -0,0 +1,360 @@ +# Description + +Explore your AWS platform with, [`Dgraph`](https://dgraph.io/), a graph database. + +![screenshot](./img/screenshot.png) + +Thanks to Go and its goroutines, we can insert thousand of ressources in few seconds. + +## Prerequisites + +- [docker](https://docs.docker.com/) +- [docker-compose](https://docs.docker.com/compose/install/) +- [Go](https://golang.org/) (for build only) + +## Build + +This project uses *go.mod*, so after cloning this repo, simply run : + +```bash +go build && chmod +x ./inventory-graph +``` + +or + +```bash +GOBIN=/usr/local/bin/ go install && chmod +x /usr/local/bin/inventory-graph +``` + +## Usage + +### Start Dgraph server + +```bash +make +``` + +or + +```bash +make up +``` + +Access to WebUI (**dgraph-ratel**) : [http://localhost:8000](http://localhost:8000) + +### Import ressources + +Authentication is based on your `.aws/config` file. + +```bash +Usage of inventory-graph: + -dgraph string + Dgraph server (ip:port) (default "127.0.0.1:9080") + -list + List available ressource types + -no-drop + Disable the drop of all nodes and the schema before import + -no-schema + Disable the refresh of the schema at each run + -profile string + Profile from ~/.aws/config (default "default") + -region string + AWS Region (default "eu-west-1") + -type string + Get the schema for a type (only after importing some data) +``` + +Example : + +```bash +inventory-graph -region us-west-2 -profile xxxx + +2019/11/29 17:35:58 Drop all previous data +2019/11/29 17:35:58 Add schema +2019/11/29 17:36:04 List ... +... +2019/11/29 17:36:05 Add ... Nodes +... +2019/11/29 17:36:08 Add ... Edges +... +``` + +:warning: For now, data are cleaned up at each execution, and only one account and one region can be imported at time . + +### Get schema for a type + +You can get all schemas for *types* and *predicates* in **dgraph-ratel** WebUI: + +![schemas](./img/schemas.png) + +or with binary, in JSON format : + +```bash +inventory-graph -type Address | jq + +{ + "types": [ + { + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "Service", + "type": "string" + }, + { + "name": "Region", + "type": "string" + }, + { + "name": "OwnerId", + "type": "string" + }, + { + "name": "PrivateIpAddress", + "type": "string" + }, + { + "name": "PublicIp", + "type": "string" + }, + { + "name": "Domain", + "type": "string" + }, + { + "name": "AllocationId", + "type": "string" + }, + { + "name": "_Instance", + "type": "Instance" + } + ], + "name": "Address" + } + ] +} +``` + +**Predicates which are prefixed with a `_` are Edges, and they all have a reverse.** + +## Stop and/or Remove Dgraph + +Stop : + +```bash +make stop +``` + +Remove : + +```bash +make rm +``` + +## Available Ressources + +Here the list of currently supported ressources : + +- Address +- AutoScalingGroup +- AvailabilityZone +- CacheCluster +- CacheSubnetGroup +- Cidr +- DbCluster +- DbClusterParameterGroup +- DbInstance +- DbParameterGroup +- DbSubnetGroup +- Instance +- InstanceProfile +- KeyPair +- LaunchConfiguration +- LaunchTemplate +- LoadBalancer +- NatGateway +- OptionGroup +- SecurityGroup +- Subnet +- TargetGroup +- Volume +- Vpc +- VpcPeeringConnection + +**All Edges between the Nodes have reversed.** + +:warning: AWS API is often messy, names are not consistent from one endpoint to another, we try to fix that and keep a global coherance. This is why some Predicates don't match exact names returned by API. + +## Query examples + +### Get Elastic IPs + Instances + NatGateways + +```bash +{ + Address(func: type(Address)) @filter(has(_Instance) or has(_NatGateway)){ + name dgraph.type PublicIp + Instance:_Instance {name dgraph.type InstanceId} + NatGateway: _NatGateway{name dgraph.type NatGatewayID} + } +} +``` + +### Get Classic LoadBalancers + AutoScalingGroups + Instances + +```bash +{ + LoadBalancer(func: type(LoadBalancer))@filter(eq(LoadBalancerType, classic)) @cascade{ + name dgraph.type + AutoScaling:~_LoadBalancer { + name dgraph.type + Instance:_Instance{ + name dgraph.type InstanceId + } + } + } +} +``` + +### Get Application LoadBalancers + TargetGroups + AutoScalingGroups + Instances + +```bash +{ + LoadBalancerV2(func: type(LoadBalancer))@filter(eq(LoadBalancerType, application))@cascade{ + name dgraph.type + TargetGroup:~_LoadBalancer @filter(type(TargetGroup)){ + name dgraph.type + AutoScalingGroup:~_TargetGroup @filter(type(AutoScalingGroup)){ + name dgraph.type Instance:_Instance{ + name dgraph.type InstanceId + } + } + } + } +} +``` + +### Get VpcPeeringConnections + Vpcs + +```bash +{ + VpcPeeringConnection(func: type(VpcPeeringConnection)){ + name dgraph.type VpcPeeringConnectionId + AccepterVpc:_AccepterVpc {name dgraph.type VpcId} + RequesterVpc:_RequesterVpc {name dgraph.type VpcId} + } +} +``` + +### Get which Instances have access to which DbInstances + +```bash +{ + DbInstances(func: type(DbInstance))@cascade{ + name dgraph.type + SecurityGroup:_SecurityGroup { + name dgraph.type GroupId + IngressSecurityGroup:_SecurityGroup @facets { + name dgraph.type + Instance:~_SecurityGroup @filter(type(Instance)){ + name dgraph.type InstanceId + } + } + } + } +} + +``` + +### Get the Cidr which are allowed for access to DbInstances + +```bash +{ + Rds(func: type(DbInstance))@cascade{ + name dgraph.type + SecurityGroup:_SecurityGroup { + name dgraph.type GroupId + IngressCidr:_Cidr @facets { + name dgraph.type + } + } + } +} +``` + +### Get Instance opened worldwide + associated ports + +```bash +{ + OpenWorldCidr(func: eq(name, "0.0.0.0/0"))@cascade{ + name dgraph.type + SecurityGroup:~_Cidr @filter(type(SecurityGroup)) @facets { + name dgraph.type GroupId + Instance:~_SecurityGroup @filter(type(Instance)){ + name dgraph.type InstanceId + } + } + } +} +``` + +### Get which KeyPairs give access to which Instances + +```bash +{ + KeyPair(func: type(KeyPair))@cascade{ + name dgraph.type + Instance:~_KeyName{ + name dgraph.type InstanceId + } + } +} +``` + +### Get CacheClusters (Elasticache) with Instances which have access to them + +```bash +{ + Memcached(func: type(CacheCluster))@filter(eq(Engine, memcached))@cascade{ + name dgraph.type + SecurityGroup:_SecurityGroup @facets { + name dgraph.type + IngressSecurityGroup:_SecurityGroup @facets { + name dgraph.type + Instance:~_SecurityGroup @filter(type(Instance)){ + name dgraph.type InstanceId + } + } + } + } + Redis(func: type(CacheCluster))@filter(eq(Engine, redis))@cascade{ + name dgraph.type + SecurityGroup:_SecurityGroup @facets { + name dgraph.type + IngressSecurityGroup:_SecurityGroup @facets { + name dgraph.type + Instance:~_SecurityGroup @filter(type(Instance)){ + name dgraph.type InstanceId + } + } + } + } +} +``` + +### Get InstanceProfiles which are not used by Instances + +```bash +{ + InstanceProfile(func: type(InstanceProfile)) @filter(not has(~_InstanceProfile)) { + name dgraph.type + } +} +``` + +## Author + +Thomas Labarussias ([@Issif](https://github.com/Issif)) diff --git a/addresses.go b/addresses.go new file mode 100644 index 0000000..3d2605a --- /dev/null +++ b/addresses.go @@ -0,0 +1,100 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type addressList struct{ *ec2.DescribeAddressesOutput } +type addressNodes []addressNode + +type addressNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + Domain string `json:"Domain,omitempty"` + PrivateIPAddress string `json:"PrivateIpAddress,omitempty"` + PublicIP string `json:"PublicIp,omitempty"` + AllocationID string `json:"AllocationID,omitempty"` + Instance instanceNodes `json:"_Instance,omitempty"` + NatGateway natGatewayNodes `json:"_NatGateway,omitempty"` +} + +func (c *connector) listAddresses() addressList { + defer c.waitGroup.Done() + + log.Println("List Addresses") + response, err := ec2.New(c.awsSession).DescribeAddresses(&ec2.DescribeAddressesInput{}) + if err != nil { + log.Fatal(err) + } + return addressList{response} +} + +func (list addressList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Addresses) == 0 { + return + } + log.Println("Add Address Nodes") + a := make(addressNodes, 0, len(list.Addresses)) + + for _, i := range list.Addresses { + var b addressNode + b.Service = "ec2" + b.Type = []string{"Address"} + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Name = *i.PublicIp + b.PublicIP = *i.PublicIp + b.AllocationID = *i.AllocationId + b.Domain = *i.Domain + if i.PrivateIpAddress != nil { + b.PrivateIPAddress = *i.PrivateIpAddress + } + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]addressNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Address"), &m) + for _, i := range m["list"] { + n[i.PublicIP] = i.UID + } + c.ressources["Addresses"] = n +} + +func (list addressList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.Addresses) == 0 { + return + } + log.Println("Add Address Edges") + + a := addressNodes{} + for _, i := range list.Addresses { + if i.AssociationId != nil { + b := addressNode{ + UID: c.ressources["Addresses"][*i.PublicIp], + NatGateway: natGatewayNodes{natGatewayNode{UID: c.ressources["NatGateways"][*i.PublicIp]}}, + } + if i.InstanceId != nil { + b.Instance = instanceNodes{instanceNode{UID: c.ressources["Instances"][*i.InstanceId]}} + } + a = append(a, b) + } + } + if len(a) != 0 { + c.dgraphAddNodes(a) + } +} diff --git a/autoscalinggroups.go b/autoscalinggroups.go new file mode 100644 index 0000000..40bdfde --- /dev/null +++ b/autoscalinggroups.go @@ -0,0 +1,134 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/autoscaling" +) + +type autoScalingGroupList struct { + *autoscaling.DescribeAutoScalingGroupsOutput +} +type autoScalingGroupNodes []autoScalingGroupNode + +type autoScalingGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Service string `json:"Service,omitempty"` + AutoScalingGroupArn string `json:"AutoScalingGroupArn,omitempty"` + AutoScalingGroupName string `json:"AutoScalingGroupName,omitempty"` + DesiredCapacity int64 `json:"DesiredCapacity,omitempty"` + MinSize int64 `json:"MinSize,omitempty"` + MaxSize int64 `json:"MaxSize,omitempty"` + DefaultCooldown int64 `json:"DefaultCooldown,omitempty"` + HealthCheckGracePeriod int64 `json:"HealthCheckGracePeriod,omitempty"` + HealthCheckType string `json:"HealthCheckType,omitempty"` + TerminationPolicy string `json:"TerminationPolicy,omitempty"` + NewInstancesProtectedFromScaleIn bool `json:"NewInstancesProtectedFromScaleIn,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + Instance instanceNodes `json:"_Instance,omitempty"` + TargetGroup targetGroupNodes `json:"_TargetGroup,omitempty"` + LoadBalancer loadBalancerNodes `json:"_LoadBalancer,omitempty"` + LaunchConfiguration launchConfigurationNodes `json:"_LaunchConfiguration,omitempty"` + LaunchTemplate launchTemplateNodes `json:"_LaunchTemplate,omitempty"` + // Subnet subnetNodes `json:"_Subnet<,omitempty"` +} + +func (c *connector) listAutoScalingGroups() autoScalingGroupList { + defer c.waitGroup.Done() + + log.Println("List AutoScalingGroups") + response, err := autoscaling.New(c.awsSession).DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return autoScalingGroupList{response} +} + +func (list autoScalingGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.AutoScalingGroups) == 0 { + return + } + log.Println("Add AutoScalingGroup Nodes") + a := make(autoScalingGroupNodes, 0, len(list.AutoScalingGroups)) + + for _, i := range list.AutoScalingGroups { + var b autoScalingGroupNode + b.Service = "autoscaling" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"AutoScalingGroup"} + b.Name = *i.AutoScalingGroupName + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.AutoScalingGroupArn = *i.AutoScalingGroupARN + b.MinSize = *i.MinSize + b.MaxSize = *i.MaxSize + b.DesiredCapacity = *i.DesiredCapacity + b.TerminationPolicy = *i.TerminationPolicies[0] + b.DefaultCooldown = *i.DefaultCooldown + b.HealthCheckGracePeriod = *i.HealthCheckGracePeriod + b.HealthCheckType = *i.HealthCheckType + b.NewInstancesProtectedFromScaleIn = *i.NewInstancesProtectedFromScaleIn + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]autoScalingGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("AutoScalingGroup"), &m) + for _, i := range m["list"] { + n[i.AutoScalingGroupArn] = i.UID + } + c.ressources["AutoScalingGroups"] = n +} + +func (list autoScalingGroupList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.AutoScalingGroups) == 0 { + return + } + log.Println("Add AutoScalingGroup Edges") + a := autoScalingGroupNodes{} + for _, i := range list.AutoScalingGroups { + b := autoScalingGroupNode{ + UID: c.ressources["AutoScalingGroups"][*i.AutoScalingGroupARN], + } + if len(i.Instances) != 0 { + for _, j := range i.Instances { + b.Instance = append(b.Instance, instanceNode{UID: c.ressources["Instances"][*j.InstanceId]}) + } + } + if len(i.TargetGroupARNs) != 0 { + for _, j := range i.TargetGroupARNs { + b.TargetGroup = append(b.TargetGroup, targetGroupNode{UID: c.ressources["TargetGroups"][*j]}) + } + } + for _, i := range i.AvailabilityZones { + b.AvailabilityZone = append(b.AvailabilityZone, availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i]}) + } + for _, i := range i.LoadBalancerNames { + b.LoadBalancer = append(b.LoadBalancer, loadBalancerNode{UID: c.ressources["LoadBalancers"][*i]}) + } + if i.LaunchConfigurationName != nil { + b.LaunchConfiguration = launchConfigurationNodes{launchConfigurationNode{UID: c.ressources["LaunchConfigurations"][*i.LaunchConfigurationName]}} + } + if i.LaunchTemplate != nil { + b.LaunchTemplate = launchTemplateNodes{launchTemplateNode{UID: c.ressources["LaunchTemplates"][*i.LaunchTemplate.LaunchTemplateId]}} + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/availibiltyzones.go b/availibiltyzones.go new file mode 100644 index 0000000..81fe24a --- /dev/null +++ b/availibiltyzones.go @@ -0,0 +1,65 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type availabilityZoneList struct { + *ec2.DescribeAvailabilityZonesOutput +} +type availabilityZoneNodes []availabilityZoneNode + +type availabilityZoneNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + State string `json:"State,omitempty"` + ZoneName string `json:"ZoneName,omitempty"` + ZoneID string `json:"ZoneId,omitempty"` +} + +func (c *connector) listAvailabilityZones() availabilityZoneList { + defer c.waitGroup.Done() + + log.Println("List AvailibiltyZones") + response, err := ec2.New(c.awsSession).DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{}) + if err != nil { + log.Fatal(err) + } + return availabilityZoneList{response} +} + +func (list availabilityZoneList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.AvailabilityZones) == 0 { + return + } + log.Println("Add AvailibilityZones Nodes") + a := make(availabilityZoneNodes, 0, len(list.AvailabilityZones)) + + for _, i := range list.AvailabilityZones { + var b availabilityZoneNode + b.Service = "ec2" + b.Type = []string{"AvailabilityZone"} + b.Region = c.awsRegion + b.Name = *i.ZoneName + b.ZoneName = *i.ZoneName + b.ZoneID = *i.ZoneId + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]availabilityZoneNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("AvailabilityZone"), &m) + for _, i := range m["list"] { + n[i.ZoneName] = i.UID + } + c.ressources["AvailabilityZones"] = n +} diff --git a/cacheclusters.go b/cacheclusters.go new file mode 100644 index 0000000..a1d6c23 --- /dev/null +++ b/cacheclusters.go @@ -0,0 +1,116 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/elasticache" +) + +type cacheClusterList struct { + *elasticache.DescribeCacheClustersOutput +} +type cacheClusterNodes []cacheClusterNode + +type cacheClusterNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + CacheClusterID string `json:"CacheClusterId,omitempty"` + Engine string `json:"Engine,omitempty"` + EngineVersion string `json:"EngineVersion,omitempty"` + AuthTokenEnabled bool `json:"AuthTokenEnabled,omitempty"` + Endpoint string `json:"Endpoint,omitempty"` + Port int64 `json:"Port,omitempty"` + AutoMinorVersionUpgrade bool `json:"AutoMinorVersionUpgrade,omitempty"` + Status string `json:"CacheClusterStatus,omitempty"` + NumCacheNodes int64 `json:"NumCacheNodes,omitempty"` + TransitEncryptionEnabled bool `json:"TransitEncryptionEnabled,omitempty"` + PreferredMaintenanceWindow string `json:"PreferredMaintenanceWindow,omitempty"` + CacheNodeType string `json:"CacheNodeType,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` + CacheSubnetGroup cacheSubnetGroupNodes `json:"_CacheSubnetGroup,omitempty"` + // CacheParameterGroup cacheParameterGroupNode `json:"_CacheParameterGroup,omitempty"` +} + +func (c *connector) listCacheClusters() cacheClusterList { + defer c.waitGroup.Done() + + log.Println("List CacheClusters") + response, err := elasticache.New(c.awsSession).DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{}) + if err != nil { + log.Fatal(err) + } + return cacheClusterList{response} +} + +func (list cacheClusterList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.CacheClusters) == 0 { + return + } + log.Println("Add CacheCluster Nodes") + a := make(cacheClusterNodes, 0, len(list.CacheClusters)) + + for _, i := range list.CacheClusters { + var b cacheClusterNode + b.Service = "elasticache" + b.Type = []string{"CacheCluster"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.CacheClusterId + b.CacheClusterID = *i.CacheClusterId + b.Engine = *i.Engine + b.EngineVersion = *i.EngineVersion + b.AuthTokenEnabled = *i.AuthTokenEnabled + if i.ConfigurationEndpoint != nil { + b.Endpoint = *i.ConfigurationEndpoint.Address + b.Port = *i.ConfigurationEndpoint.Port + } + b.AutoMinorVersionUpgrade = *i.AutoMinorVersionUpgrade + b.Status = *i.CacheClusterStatus + b.NumCacheNodes = *i.NumCacheNodes + b.TransitEncryptionEnabled = *i.TransitEncryptionEnabled + b.PreferredMaintenanceWindow = *i.PreferredMaintenanceWindow + b.CacheNodeType = *i.CacheNodeType + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]cacheClusterNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("CacheCluster"), &m) + for _, i := range m["list"] { + n[i.CacheClusterID] = i.UID + } + c.ressources["CacheClusters"] = n +} + +func (list cacheClusterList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.CacheClusters) == 0 { + return + } + log.Println("Add CacheCluster Edges") + a := cacheClusterNodes{} + for _, i := range list.CacheClusters { + b := cacheClusterNode{ + UID: c.ressources["CacheClusters"][*i.CacheClusterId], + CacheSubnetGroup: cacheSubnetGroupNodes{cacheSubnetGroupNode{UID: c.ressources["CacheSubnetGroups"][*i.CacheSubnetGroupName]}}, + AvailabilityZone: availabilityZoneNodes{availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.PreferredAvailabilityZone]}}, + } + for _, j := range i.SecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j.SecurityGroupId]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/cachesubnetgroups.go b/cachesubnetgroups.go new file mode 100644 index 0000000..c6f1ded --- /dev/null +++ b/cachesubnetgroups.go @@ -0,0 +1,91 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/elasticache" +) + +type cacheSubnetGroupList struct { + *elasticache.DescribeCacheSubnetGroupsOutput +} +type cacheSubnetGroupNodes []cacheSubnetGroupNode + +type cacheSubnetGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + CacheSubnetGroupName string `json:"CacheSubnetGroupName,omitempty"` + Description string `json:"Description,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + Subnet subnetNodes `json:"_Subnet,omitempty"` +} + +func (c *connector) listCacheSubnetGroups() cacheSubnetGroupList { + defer c.waitGroup.Done() + + log.Println("List CacheSubnetGroups") + response, err := elasticache.New(c.awsSession).DescribeCacheSubnetGroups(&elasticache.DescribeCacheSubnetGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return cacheSubnetGroupList{response} +} + +func (list cacheSubnetGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.CacheSubnetGroups) == 0 { + return + } + log.Println("Add CacheSubnetGroup Nodes") + a := make(cacheSubnetGroupNodes, 0, len(list.CacheSubnetGroups)) + + for _, i := range list.CacheSubnetGroups { + var b cacheSubnetGroupNode + b.Service = "elasticache" + b.Type = []string{"CacheSubnetGroup"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.CacheSubnetGroupName + b.CacheSubnetGroupName = *i.CacheSubnetGroupName + b.Description = *i.CacheSubnetGroupDescription + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]cacheSubnetGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("CacheSubnetGroup"), &m) + for _, i := range m["list"] { + n[i.CacheSubnetGroupName] = i.UID + } + c.ressources["CacheSubnetGroups"] = n +} + +func (list cacheSubnetGroupList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.CacheSubnetGroups) == 0 { + return + } + log.Println("Add CacheSubnetGroup Edges") + a := cacheSubnetGroupNodes{} + for _, i := range list.CacheSubnetGroups { + b := cacheSubnetGroupNode{ + UID: c.ressources["CacheSubnetGroups"][*i.CacheSubnetGroupName], + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]}, + } + for _, j := range i.Subnets { + b.Subnet = append(b.Subnet, subnetNode{UID: c.ressources["Subnets"][*j.SubnetIdentifier]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/connector.go b/connector.go new file mode 100644 index 0000000..02b4b79 --- /dev/null +++ b/connector.go @@ -0,0 +1,62 @@ +package main + +import ( + "context" + "log" + "sync" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/dgraph-io/dgo/v2" + "github.com/dgraph-io/dgo/v2/protos/api" + "google.golang.org/grpc" +) + +type connector struct { + grpcConnexion *grpc.ClientConn + dgraphClient *dgo.Dgraph + context *context.Context + awsSession *session.Session + awsRegion string + awsAccountName string + awsAccountID string + ressources map[string]map[string]string + waitGroup sync.WaitGroup +} + +func newConnector(profile, region, dgraph *string) *connector { + // Create AWS session (credentials from ~/.aws/config) + awsSession := session.Must(session.NewSessionWithOptions(session.Options{ + SharedConfigState: session.SharedConfigEnable, //enable use of ~/.aws/config + AssumeRoleTokenProvider: stscreds.StdinTokenProvider, //ask for MFA if needed + Profile: *profile, + Config: aws.Config{Region: aws.String(*region)}, + })) + + resultSts, err := sts.New(awsSession).GetCallerIdentity(&sts.GetCallerIdentityInput{}) + if err != nil { + log.Fatal(err.Error()) + } + + connexion, err := grpc.Dial(*dgraph, grpc.WithInsecure()) + if err != nil { + log.Fatal(err.Error()) + } + + dgraphClient := dgo.NewDgraphClient(api.NewDgraphClient(connexion)) + ctx := context.Background() + + return &connector{ + dgraphClient: dgraphClient, + grpcConnexion: connexion, + context: &ctx, + awsSession: awsSession, + awsRegion: *region, + awsAccountName: *profile, + awsAccountID: *resultSts.Account, + ressources: map[string]map[string]string{}, + waitGroup: sync.WaitGroup{}, + } +} diff --git a/dbclusterparametergroups.go b/dbclusterparametergroups.go new file mode 100644 index 0000000..2d2108b --- /dev/null +++ b/dbclusterparametergroups.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/rds" +) + +type dbClusterParameterGroupList struct { + *rds.DescribeDBClusterParameterGroupsOutput +} +type dbClusterParameterGroupNodes []dbClusterParameterGroupNode + +type dbClusterParameterGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + DBClusterParameterGroupArn string `json:"DbClusterParameterGroupArn,omitempty"` + DBClusterParameterGroupName string `json:"DbClusterParameterGroupName,omitempty"` + DBParameterGroupFamily string `json:"DbClusterParameterGroupFamily,omitempty"` + Description string `json:"Description,omitempty"` +} + +func (c *connector) listDbClusterParameterGroups() dbClusterParameterGroupList { + defer c.waitGroup.Done() + + log.Println("List DbClusterParameterGroups") + response, err := rds.New(c.awsSession).DescribeDBClusterParameterGroups(&rds.DescribeDBClusterParameterGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return dbClusterParameterGroupList{response} +} + +func (list dbClusterParameterGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBClusterParameterGroups) == 0 { + return + } + log.Println("Add ParameterGroup Nodes") + a := make(dbClusterParameterGroupNodes, 0, len(list.DBClusterParameterGroups)) + + for _, i := range list.DBClusterParameterGroups { + var b dbClusterParameterGroupNode + b.Service = "rds" + b.Type = []string{"DbClusterParameterGroup"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.DBClusterParameterGroupName + b.DBClusterParameterGroupName = *i.DBClusterParameterGroupName + b.DBClusterParameterGroupArn = *i.DBClusterParameterGroupArn + b.DBParameterGroupFamily = *i.DBParameterGroupFamily + b.Description = *i.Description + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]dbClusterParameterGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("DbClusterParameterGroup"), &m) + for _, i := range m["list"] { + n[i.DBClusterParameterGroupName] = i.UID + } + c.ressources["DbClusterParameterGroups"] = n +} diff --git a/dbclusters.go b/dbclusters.go new file mode 100644 index 0000000..98226bb --- /dev/null +++ b/dbclusters.go @@ -0,0 +1,144 @@ +package main + +import ( + "encoding/json" + "log" + "strings" + + "github.com/aws/aws-sdk-go/service/rds" +) + +type dbClusterList struct{ *rds.DescribeDBClustersOutput } +type dbClusterNodes []dbClusterNode + +type dbClusterNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + DBClusterArn string `json:"DbClusterArn,omitempty"` + DBClusterIdentifier string `json:"DbClusterIdentifier,omitempty"` + DBClusterResourceID string `json:"DbClusterResourceID,omitempty"` + DBName string `json:"DbName,omitempty"` + MasterUsername string `json:"MasterUsername,omitempty"` + Engine string `json:"Engine,omitempty"` + EngineMode string `json:"EngineMode,omitempty"` + EngineVersion string `json:"EngineVersion,omitempty"` + Endpoint string `json:"Endpoint,omitempty"` + ReaderEndpoint string `json:"ReaderEndpoint,omitempty"` + Port int64 `json:"Port,omitempty"` + MultiAZ bool `json:"MultiAz,omitempty"` + HTTPEndpointEnabled bool `json:"HttpEndpointEnabled,omitempty"` + IAMDatabaseAuthenticationEnabled bool `json:"IamDatabaseAuthenticationEnabled,omitempty"` + PreferredMaintenanceWindow string `json:"PreferredMaintenanceWindow,omitempty"` + DeletionProtection bool `json:"DeletionProtection,omitempty"` + HostedZoneID string `json:"HostedZoneId,omitempty"` + Status string `json:"Status,omitempty"` + PreferredBackupWindow string `json:"PreferredBackupWindow,omitempty"` + AllocatedStorage int64 `json:"AllocatedStorage,omitempty"` + BackupRetentionPeriod int64 `json:"BackupRetentionPeriod,omitempty"` + StorageEncrypted bool `json:"StorageEncrypted,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + DBClusterMember dbInstanceNodes `json:"_DbClusterMember,omitempty"` + ReadReplica dbInstanceNodes `json:"_ReadReplica,omitempty"` + DBSubnetGroup dbSubnetGroupNodes `json:"_DbSubnetGroup,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` + DBClusterParameterGroup dbClusterParameterGroupNode `json:"_DbClusterParameterGroup,omitempty"` +} + +func (c *connector) listDbClusters() dbClusterList { + defer c.waitGroup.Done() + + log.Println("List DbClusters") + response, err := rds.New(c.awsSession).DescribeDBClusters(&rds.DescribeDBClustersInput{}) + if err != nil { + log.Fatal(err) + } + return dbClusterList{response} +} + +func (list dbClusterList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBClusters) == 0 { + return + } + log.Println("Add DbCluster Nodes") + a := make(dbClusterNodes, 0, len(list.DBClusters)) + + for _, i := range list.DBClusters { + var b dbClusterNode + b.Service = "rds" + b.Type = []string{"DbCluster"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.DBClusterIdentifier + b.DBClusterArn = *i.DBClusterArn + b.DBClusterIdentifier = *i.DBClusterIdentifier + b.DBClusterResourceID = *i.DbClusterResourceId + b.MasterUsername = *i.MasterUsername + b.Engine = *i.Engine + b.EngineMode = *i.EngineMode + b.EngineVersion = *i.EngineVersion + b.Endpoint = *i.Endpoint + b.ReaderEndpoint = *i.ReaderEndpoint + b.Port = *i.Port + b.MultiAZ = *i.MultiAZ + b.HTTPEndpointEnabled = *i.HttpEndpointEnabled + b.IAMDatabaseAuthenticationEnabled = *i.IAMDatabaseAuthenticationEnabled + b.PreferredMaintenanceWindow = *i.PreferredMaintenanceWindow + b.DeletionProtection = *i.IAMDatabaseAuthenticationEnabled + b.HostedZoneID = *i.HostedZoneId + b.Status = *i.Status + b.AllocatedStorage = *i.AllocatedStorage + b.BackupRetentionPeriod = *i.BackupRetentionPeriod + b.StorageEncrypted = *i.StorageEncrypted + b.DBName = *i.DatabaseName + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]dbClusterNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("DbCluster"), &m) + for _, i := range m["list"] { + n[i.DBClusterIdentifier] = i.UID + } + c.ressources["DbClusters"] = n +} + +func (list dbClusterList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBClusters) == 0 { + return + } + log.Println("Add DbCluster Edges") + a := dbClusterNodes{} + for _, i := range list.DBClusters { + b := dbClusterNode{ + UID: c.ressources["DbClusters"][*i.DBClusterIdentifier], + DBClusterParameterGroup: dbClusterParameterGroupNode{UID: c.ressources["DbParameterGroups"][*i.DBClusterParameterGroup]}, + DBSubnetGroup: dbSubnetGroupNodes{dbSubnetGroupNode{UID: c.ressources["DbSubnetGroups"][*i.DBSubnetGroup]}}, + } + for _, j := range i.AvailabilityZones { + b.AvailabilityZone = append(b.AvailabilityZone, availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*j]}) + } + for _, j := range i.VpcSecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j.VpcSecurityGroupId]}) + } + for _, j := range i.DBClusterMembers { + b.DBClusterMember = append(b.DBClusterMember, dbInstanceNode{UID: c.ressources["DbInstances"][*j.DBInstanceIdentifier], IsClusterWriter: *j.IsClusterWriter}) + } + for _, j := range i.ReadReplicaIdentifiers { + s := strings.Split(*j, ":") + b.ReadReplica = append(b.ReadReplica, dbInstanceNode{UID: c.ressources["DbInstances"][s[6]]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/dbinstances.go b/dbinstances.go new file mode 100644 index 0000000..9739121 --- /dev/null +++ b/dbinstances.go @@ -0,0 +1,154 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/rds" +) + +type dbInstanceList struct{ *rds.DescribeDBInstancesOutput } +type dbInstanceNodes []dbInstanceNode + +type dbInstanceNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + DBInstanceArn string `json:"DbInstanceArn,omitempty"` + DBInstanceIdentifier string `json:"DbInstanceIdentifier,omitempty"` + DBName string `json:"DbName,omitempty"` + DBInstanceStatus string `json:"DbInstanceStatus,omitempty"` + DBInstanceClass string `json:"DbInstanceClass,omitempty"` + DbiResourceID string `json:"DbiResourceId,omitempty"` + CACertificateIdentifier string `json:"CACertificateIdentifier,omitempty"` + PubliclyAccessible bool `json:"PubliclyAccessible,omitempty"` + MasterUsername string `json:"MasterUsername,omitempty"` + LicenseModel string `json:"LicenseModel,omitempty"` + CopyTagsToSnapshot bool `json:"CopyTagsToSnapshot,omitempty"` + Engine string `json:"Engine,omitempty"` + EngineVersion string `json:"EngineVersion,omitempty"` + Endpoint string `json:"Endpoint,omitempty"` + Port int64 `json:"Port,omitempty"` + MultiAZ bool `json:"MultiAz,omitempty"` + AutoMinorVersionUpgrade bool `json:"AutoMinorVersionUpgrade,omitempty"` + IAMDatabaseAuthenticationEnabled bool `json:"IamDatabaseAuthenticationEnabled,omitempty"` + DeletionProtection bool `json:"DeletionProtection,omitempty"` + PreferredBackupWindow string `json:"PreferredBackupWindow,omitempty"` + PreferredMaintenanceWindow string `json:"PreferredMaintenanceWindow,omitempty"` + PromotionTier int64 `json:"PromotionTier,omitempty"` + AllocatedStorage int64 `json:"AllocatedStorage,omitempty"` + StorageType string `json:"StorageType,omitempty"` + StorageEncrypted bool `json:"StorageEncrypted,omitempty"` + BackupRetentionPeriod int64 `json:"BackupRetentionPeriod,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + SecondaryAvailabilityZone availabilityZoneNodes `json:"_SecondaryAvailabilityZone,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + OptionGroup optionGroupNode `json:"_OptionGroup,omitempty"` + IsClusterWriter bool `json:"_DbClusterMember|IsClusterWriter,omitempty"` + DBSubnetGroup dbSubnetGroupNodes `json:"_DbSubnetGroup,omitempty"` + DBParameterGroup dbParameterGroupNode `json:"_DbParameterGroup,omitempty"` +} + +func (c *connector) listDbInstances() dbInstanceList { + defer c.waitGroup.Done() + + log.Println("List DbInstances") + response, err := rds.New(c.awsSession).DescribeDBInstances(&rds.DescribeDBInstancesInput{}) + if err != nil { + log.Fatal(err) + } + return dbInstanceList{response} +} + +func (list dbInstanceList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBInstances) == 0 { + return + } + log.Println("Add Dbinstance Nodes") + a := make(dbInstanceNodes, 0, len(list.DBInstances)) + + for _, i := range list.DBInstances { + var b dbInstanceNode + b.Service = "rds" + b.Type = []string{"DbInstance"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.DBInstanceIdentifier + b.DBInstanceArn = *i.DBInstanceArn + b.DBInstanceIdentifier = *i.DBInstanceIdentifier + if i.DBName != nil { + b.DBName = *i.DBName + } + b.DBInstanceStatus = *i.DBInstanceStatus + b.DBInstanceClass = *i.DBInstanceClass + b.DbiResourceID = *i.DbiResourceId + b.CACertificateIdentifier = *i.CACertificateIdentifier + b.PubliclyAccessible = *i.PubliclyAccessible + b.MasterUsername = *i.MasterUsername + b.LicenseModel = *i.LicenseModel + b.CopyTagsToSnapshot = *i.CopyTagsToSnapshot + b.Engine = *i.Engine + b.EngineVersion = *i.EngineVersion + b.Endpoint = *i.Endpoint.Address + b.Port = *i.Endpoint.Port + b.MultiAZ = *i.MultiAZ + b.AutoMinorVersionUpgrade = *i.AutoMinorVersionUpgrade + b.IAMDatabaseAuthenticationEnabled = *i.IAMDatabaseAuthenticationEnabled + b.DeletionProtection = *i.IAMDatabaseAuthenticationEnabled + b.PreferredBackupWindow = *i.PreferredBackupWindow + b.PreferredMaintenanceWindow = *i.PreferredMaintenanceWindow + if i.PromotionTier != nil { + b.PromotionTier = *i.PromotionTier + } + b.AllocatedStorage = *i.AllocatedStorage + b.StorageType = *i.StorageType + b.StorageEncrypted = *i.StorageEncrypted + b.BackupRetentionPeriod = *i.BackupRetentionPeriod + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]dbInstanceNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("DbInstance"), &m) + for _, i := range m["list"] { + n[i.DBInstanceIdentifier] = i.UID + } + c.ressources["DbInstances"] = n +} + +func (list dbInstanceList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBInstances) == 0 { + return + } + log.Println("Add Dbinstance Edges") + a := dbInstanceNodes{} + for _, i := range list.DBInstances { + b := dbInstanceNode{ + UID: c.ressources["DbInstances"][*i.DBInstanceIdentifier], + AvailabilityZone: availabilityZoneNodes{availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.AvailabilityZone]}}, + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.DBSubnetGroup.VpcId]}, + OptionGroup: optionGroupNode{UID: c.ressources["OptionGroups"][*i.OptionGroupMemberships[0].OptionGroupName]}, + DBParameterGroup: dbParameterGroupNode{UID: c.ressources["DbParameterGroups"][*i.DBParameterGroups[0].DBParameterGroupName]}, + DBSubnetGroup: dbSubnetGroupNodes{dbSubnetGroupNode{UID: c.ressources["DbSubnetGroups"][*i.DBSubnetGroup.DBSubnetGroupName]}}, + } + if i.SecondaryAvailabilityZone != nil { + b.SecondaryAvailabilityZone = availabilityZoneNodes{availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.SecondaryAvailabilityZone]}} + } + for _, j := range i.VpcSecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j.VpcSecurityGroupId]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/dbparametergroups.go b/dbparametergroups.go new file mode 100644 index 0000000..a2c605b --- /dev/null +++ b/dbparametergroups.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/rds" +) + +type dbParameterGroupList struct { + *rds.DescribeDBParameterGroupsOutput +} +type dbParameterGroupNodes []dbParameterGroupNode + +type dbParameterGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + DBParameterGroupArn string `json:"DbParameterGroupArn,omitempty"` + DBParameterGroupName string `json:"DbParameterGroupName,omitempty"` + DBParameterGroupFamily string `json:"DbParameterGroupFamily,omitempty"` + Description string `json:"Description,omitempty"` +} + +func (c *connector) listDbParameterGroups() dbParameterGroupList { + defer c.waitGroup.Done() + + log.Println("List DbParameterGroups") + response, err := rds.New(c.awsSession).DescribeDBParameterGroups(&rds.DescribeDBParameterGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return dbParameterGroupList{response} +} + +func (list dbParameterGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBParameterGroups) == 0 { + return + } + log.Println("Add DbParameterGroup Nodes") + a := make(dbParameterGroupNodes, 0, len(list.DBParameterGroups)) + + for _, i := range list.DBParameterGroups { + var b dbParameterGroupNode + b.Service = "rds" + b.Type = []string{"DbParameterGroup"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.DBParameterGroupName + b.DBParameterGroupName = *i.DBParameterGroupName + b.DBParameterGroupArn = *i.DBParameterGroupArn + b.DBParameterGroupFamily = *i.DBParameterGroupFamily + b.Description = *i.Description + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]dbParameterGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("DbParameterGroup"), &m) + for _, i := range m["list"] { + n[i.DBParameterGroupName] = i.UID + } + c.ressources["DbParameterGroups"] = n +} diff --git a/dbsubnetgroups.go b/dbsubnetgroups.go new file mode 100644 index 0000000..a6ffef4 --- /dev/null +++ b/dbsubnetgroups.go @@ -0,0 +1,94 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/rds" +) + +type dbSubnetGroupList struct { + *rds.DescribeDBSubnetGroupsOutput +} +type dbSubnetGroupNodes []dbSubnetGroupNode + +type dbSubnetGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + DBSubnetGroupArn string `json:"DbSubnetGroupArn,omitempty"` + DBSubnetGroupName string `json:"DbSubnetGroupName,omitempty"` + SubnetGroupStatus string `json:"SubnetGroupStatus,omitempty"` + Description string `json:"Description,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + Subnet subnetNodes `json:"_Subnet,omitempty"` +} + +func (c *connector) listDbSubnetGroups() dbSubnetGroupList { + defer c.waitGroup.Done() + + log.Println("List DbSubnetGroups") + response, err := rds.New(c.awsSession).DescribeDBSubnetGroups(&rds.DescribeDBSubnetGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return dbSubnetGroupList{response} +} + +func (list dbSubnetGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBSubnetGroups) == 0 { + return + } + log.Println("Add DbSubnetGroup Nodes") + a := make(dbSubnetGroupNodes, 0, len(list.DBSubnetGroups)) + + for _, i := range list.DBSubnetGroups { + var b dbSubnetGroupNode + b.Service = "rds" + b.Type = []string{"DbSubnetGroup"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.DBSubnetGroupName + b.DBSubnetGroupName = *i.DBSubnetGroupName + b.DBSubnetGroupArn = *i.DBSubnetGroupArn + b.SubnetGroupStatus = *i.SubnetGroupStatus + b.Description = *i.DBSubnetGroupDescription + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]dbSubnetGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("DbSubnetGroup"), &m) + for _, i := range m["list"] { + n[i.DBSubnetGroupName] = i.UID + } + c.ressources["DbSubnetGroups"] = n +} + +func (list dbSubnetGroupList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.DBSubnetGroups) == 0 { + return + } + log.Println("Add DbSubnetGroup Edges") + a := dbSubnetGroupNodes{} + for _, i := range list.DBSubnetGroups { + b := dbSubnetGroupNode{ + UID: c.ressources["DbSubnetGroups"][*i.DBSubnetGroupName], + } + for _, j := range i.Subnets { + b.Subnet = append(b.Subnet, subnetNode{UID: c.ressources["Subnets"][*j.SubnetIdentifier]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/dgraph.go b/dgraph.go new file mode 100644 index 0000000..601f7d2 --- /dev/null +++ b/dgraph.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/dgraph-io/dgo/v2" + "github.com/dgraph-io/dgo/v2/protos/api" + "google.golang.org/grpc" +) + +func (c *connector) dgraphDropAll() { + log.Println("Drop all previous data") + op := api.Operation{DropAll: true} + if err := c.dgraphClient.Alter(*c.context, &op); err != nil { + log.Fatal(err) + } +} + +func (c *connector) dgraphDropPrevious() { + log.Println("List previous data") + txn := c.dgraphClient.NewTxn() + defer txn.Discard(*c.context) + + q := `query query($owner: string, $region: string){ + list(func: eq(OwnerId, $owner)) @filter(eq(Region, $region)) { + uid + } + }` + + res, err := txn.QueryWithVars(*c.context, q, map[string]string{"$owner": c.awsAccountID, "$region": c.awsRegion}) + if err != nil { + log.Println(err.Error()) + } + + m := make(map[string]cidrNodes) + json.Unmarshal(res.Json, &m) + + if len(m["list"]) != 0 { + log.Println("Drop previous data") + n, _ := json.Marshal(m["list"]) + mu := &api.Mutation{ + CommitNow: true, + DeleteJson: n, + } + + _, err = txn.Mutate(*c.context, mu) + if err != nil { + log.Fatal(err) + } + } +} + +func (c *connector) dgraphAddSchema() { + log.Println("Add schema") + op := &api.Operation{Schema: getdgraphSchema()} + err := c.dgraphClient.Alter(*c.context, op) + if err != nil { + log.Fatal(err) + } +} + +func dgraphDisplaySchema(dgraph, dtype *string) { + connexion, err := grpc.Dial(*dgraph, grpc.WithInsecure()) + if err != nil { + log.Fatal(err.Error()) + } + + dgraphClient := dgo.NewDgraphClient(api.NewDgraphClient(connexion)) + ctx := context.Background() + resp, err := dgraphClient.NewReadOnlyTxn().Query(ctx, `schema(type: [`+*dtype+`]) {type}`) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(resp.Json)) +} + +func (c *connector) dgraphAddNodes(nodes interface{}) { + txn := c.dgraphClient.NewTxn() + defer txn.Discard(*c.context) + n, err := json.Marshal(nodes) + if err != nil { + log.Fatal(err) + } + + mu := &api.Mutation{ + CommitNow: true, + SetJson: n, + } + _, err = txn.Mutate(*c.context, mu) + if err != nil { + log.Fatal(err) + } +} + +func (c *connector) dgraphQuery(nodeType string) []byte { + txn := c.dgraphClient.NewReadOnlyTxn() + defer txn.Discard(*c.context) + + q := `query query($type: string){ + list(func: type($type)) { + uid + dgraph.type + expand(_all_) + } + }` + + res, err := txn.QueryWithVars(*c.context, q, map[string]string{"$type": nodeType}) + if err != nil { + log.Println(err.Error()) + } + return res.Json +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d2a26c2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.2" +services: + dgraph: + image: dgraph/standalone:latest + ports: + - 9080:9080 + - 5080:5080 + - 6080:6080 + - 8000:8000 + - 8080:8080 + restart: on-failure \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..af780a3 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module git.fr.clara.net/claranet/cloudnative/projects/cloud/aws/tools/inventory-graph + +go 1.13 + +require ( + github.com/aws/aws-sdk-go v1.25.38 + github.com/dgraph-io/dgo/v2 v2.1.0 + google.golang.org/grpc v1.25.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bec9219 --- /dev/null +++ b/go.sum @@ -0,0 +1,75 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aws/aws-sdk-go v1.25.38 h1:QfclT79PFWCyaPDq9+zTEWsOMDWFswTpP9i07YxqPf0= +github.com/aws/aws-sdk-go v1.25.38/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/dgo/v2 v2.1.0 h1:zm4gygHzZwu11y3VqDG/fDA80xgpSvLMlrhEcb9KfT0= +github.com/dgraph-io/dgo/v2 v2.1.0/go.mod h1:R/MTZMGhTo60XSziuKpLXzI1OnVWQCZS5oJjxA8Q1bo= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +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= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/images.go b/images.go new file mode 100644 index 0000000..89581b1 --- /dev/null +++ b/images.go @@ -0,0 +1,104 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" +) + +type imageList struct{ *ec2.DescribeImagesOutput } +type imageNodes []imageNode + +type imageNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + ImageID string `json:"ImageId,omitempty"` + VirtualizationType string `json:"VirtualizationType,omitempty"` + Hypervisor string `json:"Hypervisor,omitempty"` + EnaSupport bool `json:"EnaSupport,omitempty"` + SriovNetSupport string `json:"SriovNetSupport,omitempty"` + State string `json:"State,omitempty"` + Architecture string `json:"Architecture,omitempty"` + ImageLocation string `json:"ImageLocation,omitempty"` + RootDeviceType string `json:"RootDeviceType,omitempty"` + RootDeviceName string `json:"RootDeviceName,omitempty"` + Public bool `json:"Public,omitempty"` + ImageType string `json:"ImageType,omitempty"` +} + +func (c *connector) listImages() imageList { + defer c.waitGroup.Done() + + log.Println("List Images") + response, err := ec2.New(c.awsSession).DescribeImages(&ec2.DescribeImagesInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("owner-id"), + Values: []*string{aws.String(c.awsAccountID)}, + }, + }, + }) + if err != nil { + log.Fatal(err) + } + return imageList{response} +} + +func (list imageList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Images) == 0 { + return + } + log.Println("Add Image Nodes") + a := make(imageNodes, 0, len(list.Images)) + + for _, i := range list.Images { + var b imageNode + b.Service = "ec2" + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Type = []string{"Image"} + b.Name = *i.ImageId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.ImageID = *i.ImageId + b.VirtualizationType = *i.VirtualizationType + b.Hypervisor = *i.Hypervisor + if i.EnaSupport != nil { + b.EnaSupport = *i.EnaSupport + } + if i.SriovNetSupport != nil { + b.SriovNetSupport = *i.SriovNetSupport + } + b.State = *i.State + b.Architecture = *i.Architecture + b.ImageLocation = *i.ImageLocation + b.RootDeviceType = *i.RootDeviceType + b.RootDeviceName = *i.RootDeviceName + b.Public = *i.Public + b.ImageType = *i.ImageType + a = append(a, b) + } + + c.dgraphAddNodes(a) + + m := make(map[string]imageNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Image"), &m) + for _, i := range m["list"] { + n[i.ImageID] = i.UID + } + c.ressources["Images"] = n +} diff --git a/img/schemas.png b/img/schemas.png new file mode 100644 index 0000000000000000000000000000000000000000..7ffa0e14aed9f97357580f76f88ce63c28dfb8f5 GIT binary patch literal 113078 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 literal 0 HcmV?d00001 diff --git a/img/screenshot.png b/img/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..32badcea2745c56b5cacbf19dfb2349b01ee7599 GIT binary patch literal 121200 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 bze#ZK*Xzz#qJf3BCRv!+UMjut$HV^xLicHu literal 0 HcmV?d00001 diff --git a/instanceprofiles.go b/instanceprofiles.go new file mode 100644 index 0000000..5270a71 --- /dev/null +++ b/instanceprofiles.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/iam" +) + +type instanceProfileList struct { + *iam.ListInstanceProfilesOutput +} +type instanceProfileNodes []instanceProfileNode + +type instanceProfileNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + + Service string `json:"Service,omitempty"` + InstanceProfileID string `json:"InstanceProfileId,omitempty"` + InstanceProfileName string `json:"InstanceProfileName,omitempty"` + Arn string `json:"Arn,omitempty"` +} + +func (c *connector) listInstanceProfiles() instanceProfileList { + defer c.waitGroup.Done() + + log.Println("List InstanceProfiles") + response, err := iam.New(c.awsSession).ListInstanceProfiles(&iam.ListInstanceProfilesInput{}) + if err != nil { + log.Fatal(err) + } + return instanceProfileList{response} +} + +func (list instanceProfileList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.InstanceProfiles) == 0 { + return + } + log.Println("Add InstanceProfiles Nodes") + a := make(instanceProfileNodes, 0, len(list.InstanceProfiles)) + + for _, i := range list.InstanceProfiles { + var b instanceProfileNode + b.Service = "iam" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"InstanceProfile"} + b.Name = *i.InstanceProfileName + b.InstanceProfileName = *i.InstanceProfileName + b.InstanceProfileID = *i.InstanceProfileId + b.Arn = *i.Arn + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]instanceProfileNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("InstanceProfile"), &m) + for _, i := range m["list"] { + n[i.InstanceProfileID] = i.UID + n[i.InstanceProfileName] = i.UID + } + c.ressources["InstanceProfiles"] = n +} diff --git a/instances.go b/instances.go new file mode 100644 index 0000000..18c5d7b --- /dev/null +++ b/instances.go @@ -0,0 +1,133 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type instanceList struct{ *ec2.DescribeInstancesOutput } +type instanceNodes []instanceNode + +type instanceNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + InstanceID string `json:"InstanceId,omitempty"` + InstanceType string `json:"InstanceType,omitempty"` + State string `json:"State,omitempty"` + EbsOptimized bool `json:"EbsOptimized,omitempty"` + Hypervisor string `json:"Hypervisor,omitempty"` + VirtualizationType string `json:"VirtualizationType,omitempty"` + PrivateIPAddress string `json:"PrivateIpAddress,omitempty"` + PublicIPAddress string `json:"PublicIpAddress,omitempty"` + KeyName keyPairNode `json:"_KeyName,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + InstanceProfile instanceProfileNode `json:"_InstanceProfile,omitempty"` + Image imageNode `json:"_Image,omitempty"` + Subnet subnetNodes `json:"_Subnet,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` +} + +func (c *connector) listInstances() instanceList { + defer c.waitGroup.Done() + + log.Println("List Instances") + response, err := ec2.New(c.awsSession).DescribeInstances(&ec2.DescribeInstancesInput{}) + if err != nil { + log.Fatal(err) + } + return instanceList{response} +} + +func (list instanceList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Reservations) == 0 { + return + } + log.Println("Add Instance Nodes") + instances := make(instanceNodes, 0, len(list.Reservations)) + + for _, r := range list.Reservations { + for _, i := range r.Instances { + var b instanceNode + b.Service = "ec2" + b.Type = []string{"Instance"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.InstanceId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.InstanceID = *i.InstanceId + b.InstanceType = *i.InstanceType + b.State = *i.State.Name + b.VirtualizationType = *i.VirtualizationType + b.Hypervisor = *i.Hypervisor + b.EbsOptimized = *i.EbsOptimized + if i.PrivateIpAddress != nil { + b.PrivateIPAddress = *i.PrivateIpAddress + } + if i.PublicIpAddress != nil { + b.PublicIPAddress = *i.PublicIpAddress + } + instances = append(instances, b) + } + } + c.dgraphAddNodes(instances) + + m := make(map[string]instanceNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Instance"), &m) + for _, i := range m["list"] { + n[i.InstanceID] = i.UID + } + c.ressources["Instances"] = n +} + +func (list instanceList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.Reservations) == 0 { + return + } + log.Println("Add Instance Edges") + a := instanceNodes{} + for _, r := range list.Reservations { + for _, i := range r.Instances { + b := instanceNode{ + UID: c.ressources["Instances"][*i.InstanceId], + + Image: imageNode{UID: c.ressources["Images"][*i.ImageId]}, + AvailabilityZone: availabilityZoneNodes{availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.Placement.AvailabilityZone]}}, + } + if i.KeyName != nil { + b.KeyName = keyPairNode{UID: c.ressources["KeyPairs"][*i.KeyName]} + } + if i.SubnetId != nil { + b.Subnet = subnetNodes{subnetNode{UID: c.ressources["Subnets"][*i.SubnetId]}} + } + if i.IamInstanceProfile != nil { + b.InstanceProfile = instanceProfileNode{UID: c.ressources["InstanceProfiles"][*i.IamInstanceProfile.Id]} + } + if i.VpcId != nil { + b.Vpc = vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]} + } + for _, j := range i.SecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j.GroupId]}) + } + a = append(a, b) + } + } + c.dgraphAddNodes(a) +} diff --git a/keypairs.go b/keypairs.go new file mode 100644 index 0000000..fce04b3 --- /dev/null +++ b/keypairs.go @@ -0,0 +1,66 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type keyPairList struct{ *ec2.DescribeKeyPairsOutput } +type keyPairNodes []keyPairNode + +type keyPairNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + KeyName string `json:"KeyName,omitempty"` + KeyFingerprint string `json:"KeyFingerPrint,omitempty"` +} + +func (c *connector) listKeyPairs() keyPairList { + defer c.waitGroup.Done() + + log.Println("List KeyPair Nodes") + response, err := ec2.New(c.awsSession).DescribeKeyPairs(&ec2.DescribeKeyPairsInput{}) + if err != nil { + log.Fatal(err) + } + return keyPairList{response} +} + +func (list keyPairList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.KeyPairs) == 0 { + return + } + log.Println("Add KeyPair Nodes") + a := make(keyPairNodes, 0, len(list.KeyPairs)) + + for _, i := range list.KeyPairs { + var b keyPairNode + b.Service = "ec2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"KeyPair"} + b.Name = *i.KeyName + b.KeyName = *i.KeyName + b.KeyFingerprint = *i.KeyFingerprint + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]keyPairNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("KeyPair"), &m) + for _, i := range m["list"] { + n[i.KeyName] = i.UID + } + c.ressources["KeyPairs"] = n +} diff --git a/launchconfigurations.go b/launchconfigurations.go new file mode 100644 index 0000000..944af9b --- /dev/null +++ b/launchconfigurations.go @@ -0,0 +1,107 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/autoscaling" +) + +type launchConfigurationList struct { + *autoscaling.DescribeLaunchConfigurationsOutput +} +type launchConfigurationNodes []launchConfigurationNode + +type launchConfigurationNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Service string `json:"Service,omitempty"` + LaunchConfigurationArn string `json:"LaunchConfigurationArn,omitempty"` + LaunchConfigurationName string `json:"LaunchConfigurationName,omitempty"` + InstanceType string `json:"InstanceType,omitempty"` + UserData string `json:"UserData,omitempty"` + EbsOptimized bool `json:"EbsOptimized,omitempty"` + AssociatePublicIPAddress bool `json:"AssociatePublicIpAddress,omitempty"` + KeyName keyPairNode `json:"_KeyName,omitempty"` + InstanceProfile instanceProfileNode `json:"_InstanceProfile,omitempty"` + Image imageNode `json:"_Image,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` +} + +func (c *connector) listLaunchConfigurations() launchConfigurationList { + defer c.waitGroup.Done() + + log.Println("List LaunchConfigurations") + response, err := autoscaling.New(c.awsSession).DescribeLaunchConfigurations(&autoscaling.DescribeLaunchConfigurationsInput{}) + if err != nil { + log.Fatal(err) + } + return launchConfigurationList{response} +} + +func (list launchConfigurationList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.LaunchConfigurations) == 0 { + return + } + log.Println("Add LaunchConfiguration Nodes") + a := make(launchConfigurationNodes, 0, len(list.LaunchConfigurations)) + + for _, i := range list.LaunchConfigurations { + var b launchConfigurationNode + b.Service = "autoscaling" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"LaunchConfiguration"} + b.Name = *i.LaunchConfigurationName + b.LaunchConfigurationArn = *i.LaunchConfigurationARN + b.LaunchConfigurationName = *i.LaunchConfigurationName + b.InstanceType = *i.InstanceType + b.UserData = *i.UserData + b.EbsOptimized = *i.EbsOptimized + if i.AssociatePublicIpAddress != nil { + b.AssociatePublicIPAddress = *i.AssociatePublicIpAddress + } + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]launchConfigurationNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("LaunchConfiguration"), &m) + for _, i := range m["list"] { + n[i.LaunchConfigurationName] = i.UID + } + c.ressources["LaunchConfigurations"] = n +} + +func (list launchConfigurationList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.LaunchConfigurations) == 0 { + return + } + log.Println("Add LaunchConfiguration Edges") + a := launchConfigurationNodes{} + for _, i := range list.LaunchConfigurations { + b := launchConfigurationNode{ + UID: c.ressources["LaunchConfigurations"][*i.LaunchConfigurationName], + KeyName: keyPairNode{UID: c.ressources["KeyPairs"][*i.KeyName]}, + Image: imageNode{UID: c.ressources["Images"][*i.ImageId]}, + } + if i.IamInstanceProfile != nil { + b.InstanceProfile = instanceProfileNode{UID: c.ressources["InstanceProfiles"][*i.IamInstanceProfile]} + } + for _, j := range i.SecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/launchtemplates.go b/launchtemplates.go new file mode 100644 index 0000000..679e701 --- /dev/null +++ b/launchtemplates.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type launchTemplateList struct { + *ec2.DescribeLaunchTemplatesOutput +} +type launchTemplateNodes []launchTemplateNode + +type launchTemplateNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Service string `json:"Service,omitempty"` + LaunchTemplateID string `json:"LaunchTemplateId,omitempty"` + LaunchTemplateName string `json:"LaunchTemplateName,omitempty"` + LatestVersionNumber int64 `json:"LatestVersionNumber,omitempty"` + DefaultVersionNumber int64 `json:"DefaultVersionNumber,omitempty"` + CreatedBy string `json:"CreatedBy,omitempty"` +} + +func (c *connector) listLaunchTemplates() launchTemplateList { + defer c.waitGroup.Done() + + log.Println("List LaunchTemplates") + response, err := ec2.New(c.awsSession).DescribeLaunchTemplates(&ec2.DescribeLaunchTemplatesInput{}) + if err != nil { + log.Fatal(err) + } + return launchTemplateList{response} +} + +func (list launchTemplateList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.LaunchTemplates) == 0 { + return + } + log.Println("Add LaunchTemplate Nodes") + a := make(launchTemplateNodes, 0, len(list.LaunchTemplates)) + + for _, i := range list.LaunchTemplates { + var b launchTemplateNode + b.Service = "ec2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"LaunchTemplate"} + b.Name = *i.LaunchTemplateName + b.LaunchTemplateName = *i.LaunchTemplateName + b.LaunchTemplateID = *i.LaunchTemplateId + b.LatestVersionNumber = *i.LatestVersionNumber + b.DefaultVersionNumber = *i.DefaultVersionNumber + b.CreatedBy = *i.CreatedBy + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]launchTemplateNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("LaunchTemplate"), &m) + for _, i := range m["list"] { + n[i.LaunchTemplateID] = i.UID + } + c.ressources["LaunchTemplates"] = n +} diff --git a/loadbalancers.go b/loadbalancers.go new file mode 100644 index 0000000..b106575 --- /dev/null +++ b/loadbalancers.go @@ -0,0 +1,199 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/elb" + "github.com/aws/aws-sdk-go/service/elbv2" +) + +type loadBalancerList struct { + *elb.DescribeLoadBalancersOutput +} + +type loadBalancerV2List struct { + *elbv2.DescribeLoadBalancersOutput +} + +type loadBalancerNodes []loadBalancerNode + +type loadBalancerNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Service string `json:"Service,omitempty"` + LoadBalancerName string `json:"LoadBalancerName,omitempty"` + LoadBalancerArn string `json:"LoadBalancerArn,omitempty"` + CanonicalHostedZoneID string `json:"CanonicalHostedZoneId,omitempty"` + CanonicalHostedZoneNameID string `json:"CanonicalHostedZoneNameId,omitempty"` + CanonicalHostedZoneName string `json:"CanonicalHostedZoneName,omitempty"` + DNSName string `json:"DNSName,omitempty"` + Scheme string `json:"Scheme,omitempty"` + LoadBalancerType string `json:"LoadBalancerType,omitempty"` + IPAddressType string `json:"IpAddressType,omitempty"` + State string `json:"State,omitempty"` + Instance instanceNodes `json:"_Instance,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` + Subnet subnetNodes `json:"_Subnet,omitempty"` +} + +func (c *connector) listLoadBalancers() loadBalancerList { + defer c.waitGroup.Done() + + log.Println("List LoadBalancers") + response, err := elb.New(c.awsSession).DescribeLoadBalancers(&elb.DescribeLoadBalancersInput{}) + if err != nil { + log.Fatal(err) + } + return loadBalancerList{response} +} + +func (c *connector) listLoadBalancersV2() loadBalancerV2List { + defer c.waitGroup.Done() + + log.Println("List LoadBalancersV2") + response, err := elbv2.New(c.awsSession).DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{}) + if err != nil { + log.Fatal(err) + } + return loadBalancerV2List{response} +} + +func (list loadBalancerList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.LoadBalancerDescriptions) == 0 { + return + } + log.Println("Add LoadBalancer Nodes") + a := make(loadBalancerNodes, 0, len(list.LoadBalancerDescriptions)) + + for _, i := range list.LoadBalancerDescriptions { + var b loadBalancerNode + b.Service = "elb" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"LoadBalancer"} + b.LoadBalancerType = "classic" + b.Name = *i.LoadBalancerName + b.LoadBalancerName = *i.LoadBalancerName + b.CanonicalHostedZoneNameID = *i.CanonicalHostedZoneNameID + if i.CanonicalHostedZoneName != nil { + b.CanonicalHostedZoneName = *i.CanonicalHostedZoneName + } + b.DNSName = *i.DNSName + b.Scheme = *i.Scheme + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]loadBalancerNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("LoadBalancer"), &m) + for _, i := range m["list"] { + n[i.LoadBalancerName] = i.UID + } + c.ressources["LoadBalancers"] = n +} + +func (list loadBalancerV2List) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.LoadBalancers) == 0 { + return + } + log.Println("Add LoadBalancerV2 Nodes") + a := make(loadBalancerNodes, 0, len(list.LoadBalancers)) + + for _, i := range list.LoadBalancers { + var b loadBalancerNode + b.Service = "elbv2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"LoadBalancer"} + b.LoadBalancerType = *i.Type + b.Name = *i.LoadBalancerName + b.LoadBalancerName = *i.LoadBalancerName + b.LoadBalancerArn = *i.LoadBalancerArn + b.CanonicalHostedZoneID = *i.CanonicalHostedZoneId + b.IPAddressType = *i.IpAddressType + b.State = *i.State.Code + b.Name = *i.DNSName + b.Scheme = *i.Scheme + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]loadBalancerNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("LoadBalancer"), &m) + for _, i := range m["list"] { + n[i.LoadBalancerArn] = i.UID + } + c.ressources["LoadBalancersV2"] = n +} + +func (list loadBalancerList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.LoadBalancerDescriptions) == 0 { + return + } + log.Println("Add LoadBalancer Edges") + a := loadBalancerNodes{} + for _, i := range list.LoadBalancerDescriptions { + b := loadBalancerNode{ + UID: c.ressources["LoadBalancers"][*i.LoadBalancerName], + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.VPCId]}, + } + if len(i.Instances) != 0 { + for _, j := range i.Instances { + b.Instance = append(b.Instance, instanceNode{UID: c.ressources["Instances"][*j.InstanceId]}) + } + } + for _, i := range i.AvailabilityZones { + b.AvailabilityZone = append(b.AvailabilityZone, availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i]}) + } + for _, i := range i.Subnets { + b.Subnet = append(b.Subnet, subnetNode{UID: c.ressources["Subnets"][*i]}) + } + for _, j := range i.SecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} + +func (list loadBalancerV2List) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.LoadBalancers) == 0 { + return + } + log.Println("Add LoadBalancerV2 Edges") + a := loadBalancerNodes{} + for _, i := range list.LoadBalancers { + b := loadBalancerNode{ + UID: c.ressources["LoadBalancersV2"][*i.LoadBalancerArn], + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]}, + } + for _, i := range i.AvailabilityZones { + b.AvailabilityZone = append(b.AvailabilityZone, availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.ZoneName]}) + b.Subnet = append(b.Subnet, subnetNode{UID: c.ressources["Subnets"][*i.SubnetId]}) + } + for _, j := range i.SecurityGroups { + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: c.ressources["SecurityGroups"][*j]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..64ccb20 --- /dev/null +++ b/main.go @@ -0,0 +1,156 @@ +package main + +import ( + "flag" + "fmt" + "os" +) + +func main() { + profile := flag.String("profile", "default", "Profile from ~/.aws/config") + region := flag.String("region", "eu-west-1", "AWS Region") + dgraph := flag.String("dgraph", "127.0.0.1:9080", "Dgraph server (ip:port)") + dtype := flag.String("type", "", "Get the schema for a type (only after importing some data)") + list := flag.Bool("list", false, "List available ressource types") + nodrop := flag.Bool("no-drop", false, "Disable the drop of all nodes and the schema before import") + noschema := flag.Bool("no-schema", false, "Disable the refresh schema at each run") + flag.Parse() + + if *dtype != "" { + dgraphDisplaySchema(dgraph, dtype) + os.Exit(0) + } + + connector := newConnector(profile, region, dgraph) + defer connector.grpcConnexion.Close() + + if *list == true { + fmt.Println("Address, AutoScalingGroup, AvailabilityZone, CacheCluster, CacheSubnetGroup, Cidr, DbCluster, DbClusterParameterGroup, DbInstance, DbParameterGroup, DbSubnetGroup, Instance, InstanceProfile, KeyPair, LaunchConfiguration, LaunchTemplate, LoadBalancer, NatGateway, OptionGroup, SecurityGroup, Subnet, TargetGroup, Volume, Vpc, VpcPeeringConnection") + os.Exit(0) + } + + if *nodrop != true { + connector.dgraphDropAll() + } else { + connector.dgraphDropPrevious() + } + + if *noschema != true { + connector.dgraphAddSchema() + } + + var instances instanceList + var keypairs keyPairList + var volumes volumeList + var addresses addressList + var availabilityzones availabilityZoneList + var vpcs vpcList + var instanceprofiles instanceProfileList + var autoscalinggroups autoScalingGroupList + var launchconfigurations launchConfigurationList + var launchtemplates launchTemplateList + var targetgroups targetGroupList + var loadbalancers loadBalancerList + var loadbalancersv2 loadBalancerV2List + var subnets subnetList + var securitygroups securityGroupList + var dbinstances dbInstanceList + var dbclusters dbClusterList + var optiongroups optionGroupList + var dbparametergroups dbParameterGroupList + var dbclusterparametergroups dbClusterParameterGroupList + var dbsubnetgroups dbSubnetGroupList + var cacheclusters cacheClusterList + var cachesubnetgroups cacheSubnetGroupList + var vpcpeeringconnections vpcPeeringConnectionList + var natgateways natGatewayList + // var images imageList + // var snapshots snapshotList + + // List ressources + connector.waitGroup.Add(25) + go func() { instances = connector.listInstances() }() + go func() { keypairs = connector.listKeyPairs() }() + go func() { volumes = connector.listVolumes() }() + go func() { addresses = connector.listAddresses() }() + go func() { availabilityzones = connector.listAvailabilityZones() }() + go func() { vpcs = connector.listVpcs() }() + go func() { instanceprofiles = connector.listInstanceProfiles() }() + go func() { autoscalinggroups = connector.listAutoScalingGroups() }() + go func() { launchconfigurations = connector.listLaunchConfigurations() }() + go func() { launchtemplates = connector.listLaunchTemplates() }() + go func() { targetgroups = connector.listTargetGroups() }() + go func() { loadbalancers = connector.listLoadBalancers() }() + go func() { loadbalancersv2 = connector.listLoadBalancersV2() }() + go func() { subnets = connector.listSubnets() }() + go func() { securitygroups = connector.listSecurityGroups() }() + go func() { dbinstances = connector.listDbInstances() }() + go func() { dbclusters = connector.listDbClusters() }() + go func() { optiongroups = connector.listOptionGroups() }() + go func() { dbparametergroups = connector.listDbParameterGroups() }() + go func() { dbclusterparametergroups = connector.listDbClusterParameterGroups() }() + go func() { dbsubnetgroups = connector.listDbSubnetGroups() }() + go func() { cacheclusters = connector.listCacheClusters() }() + go func() { cachesubnetgroups = connector.listCacheSubnetGroups() }() + go func() { vpcpeeringconnections = connector.listVpcPeeringConnections() }() + go func() { natgateways = connector.listNatGateways() }() + // images = connector.listImages() + // snapshots = connector.listSnapshots() + + connector.waitGroup.Wait() + + // Add Nodes + connector.waitGroup.Add(25) + instances.addNodes(connector) + keypairs.addNodes(connector) + volumes.addNodes(connector) + addresses.addNodes(connector) + availabilityzones.addNodes(connector) + vpcs.addNodes(connector) + instanceprofiles.addNodes(connector) + autoscalinggroups.addNodes(connector) + launchconfigurations.addNodes(connector) + launchtemplates.addNodes(connector) + targetgroups.addNodes(connector) + loadbalancers.addNodes(connector) + loadbalancersv2.addNodes(connector) + subnets.addNodes(connector) + securitygroups.addNodes(connector) + dbinstances.addNodes(connector) + dbclusters.addNodes(connector) + optiongroups.addNodes(connector) + dbparametergroups.addNodes(connector) + dbclusterparametergroups.addNodes(connector) + dbsubnetgroups.addNodes(connector) + cacheclusters.addNodes(connector) + cachesubnetgroups.addNodes(connector) + vpcpeeringconnections.addNodes(connector) + natgateways.addNodes(connector) + // images.addNodes(connector) + // snapshots.addNodes(connector) + + connector.waitGroup.Wait() + + // Add Edges + connector.waitGroup.Add(17) + instances.addEdges(connector) + addresses.addEdges(connector) + volumes.addEdges(connector) + autoscalinggroups.addEdges(connector) + launchconfigurations.addEdges(connector) + loadbalancers.addEdges(connector) + loadbalancersv2.addEdges(connector) + targetgroups.addEdges(connector) + subnets.addEdges(connector) + securitygroups.addEdges(connector) + dbinstances.addEdges(connector) + dbsubnetgroups.addEdges(connector) + dbclusters.addEdges(connector) + cacheclusters.addEdges(connector) + cachesubnetgroups.addEdges(connector) + vpcpeeringconnections.addEdges(connector) + natgateways.addEdges(connector) + // snapshots.addEdges(connector) + + connector.waitGroup.Wait() +} diff --git a/natgateways.go b/natgateways.go new file mode 100644 index 0000000..9f1b5cc --- /dev/null +++ b/natgateways.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type natGatewayList struct { + *ec2.DescribeNatGatewaysOutput +} +type natGatewayNodes []natGatewayNode + +type natGatewayNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + Service string `json:"Service,omitempty"` + NatGatewayID string `json:"NatGatewayId,omitempty"` + PublicIPAddress string `json:"PublicIpAddress,omitempty"` + PrivateIPAddress string `json:"PrivateIpAddress,omitempty"` + State string `json:"State,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + Subnet subnetNodes `json:"_Subnet,omitempty"` +} + +func (c *connector) listNatGateways() natGatewayList { + defer c.waitGroup.Done() + + log.Println("List NatGateways") + response, err := ec2.New(c.awsSession).DescribeNatGateways(&ec2.DescribeNatGatewaysInput{}) + if err != nil { + log.Fatal(err) + } + return natGatewayList{response} +} + +func (list natGatewayList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.NatGateways) == 0 { + return + } + log.Println("Add NatGateway Nodes") + a := make(natGatewayNodes, 0, len(list.NatGateways)) + + for _, i := range list.NatGateways { + var b natGatewayNode + b.Service = "ec2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"NatGateway"} + b.Name = *i.NatGatewayId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.NatGatewayID = *i.NatGatewayId + b.State = *i.State + b.PublicIPAddress = *i.NatGatewayAddresses[0].PublicIp + b.PrivateIPAddress = *i.NatGatewayAddresses[0].PrivateIp + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]natGatewayNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("NatGateway"), &m) + for _, i := range m["list"] { + n[i.PublicIPAddress] = i.UID + } + c.ressources["NatGateways"] = n +} + +func (list natGatewayList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.NatGateways) == 0 { + return + } + log.Println("Add NatGateway Edges") + a := natGatewayNodes{} + for _, i := range list.NatGateways { + b := natGatewayNode{ + UID: c.ressources["NatGateways"][*i.NatGatewayAddresses[0].PublicIp], + Subnet: subnetNodes{subnetNode{UID: c.ressources["Subnets"][*i.SubnetId]}}, + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]}, + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/optionsgroups.go b/optionsgroups.go new file mode 100644 index 0000000..d4e1d1f --- /dev/null +++ b/optionsgroups.go @@ -0,0 +1,76 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/rds" +) + +type optionGroupList struct { + *rds.DescribeOptionGroupsOutput +} +type optionGroupNodes []optionGroupNode + +type optionGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + OptionGroupArn string `json:"OptionGroupArn,omitempty"` + OptionGroupName string `json:"OptionGroupName,omitempty"` + Description string `json:"Description,omitempty"` + MajorEngineVersion string `json:"MajorEngineVersion,omitempty"` + EngineName string `json:"EngineName,omitempty"` + AllowsVpcAndNonVpcInstanceMemberships bool `json:"AllowsVpcAndNonVpcInstanceMemberships,omitempty"` +} + +func (c *connector) listOptionGroups() optionGroupList { + defer c.waitGroup.Done() + + log.Println("List OptionGroups") + response, err := rds.New(c.awsSession).DescribeOptionGroups(&rds.DescribeOptionGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return optionGroupList{response} +} + +func (list optionGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.OptionGroupsList) == 0 { + return + } + log.Println("Add OptionGroup Nodes") + a := make(optionGroupNodes, 0, len(list.OptionGroupsList)) + + for _, i := range list.OptionGroupsList { + var b optionGroupNode + b.Service = "rds" + b.Type = []string{"OptionGroup"} + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Name = *i.OptionGroupName + b.OptionGroupName = *i.OptionGroupName + b.OptionGroupArn = *i.OptionGroupArn + b.Description = *i.OptionGroupDescription + b.MajorEngineVersion = *i.MajorEngineVersion + b.EngineName = *i.EngineName + b.AllowsVpcAndNonVpcInstanceMemberships = *i.AllowsVpcAndNonVpcInstanceMemberships + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]optionGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("OptionGroup"), &m) + for _, i := range m["list"] { + n[i.OptionGroupName] = i.UID + } + c.ressources["OptionGroups"] = n +} diff --git a/schema.go b/schema.go new file mode 100644 index 0000000..1a9827b --- /dev/null +++ b/schema.go @@ -0,0 +1,661 @@ +package main + +func getdgraphSchema() string { + s := ` + name: string @index(exact) . + OwnerId: string @index(term) . + OwnerName: string @index(term) . + Region: string @index(term) . + Service: string @index(term) . + AvailabilityZone: string @index(term) . + State: string @index(term) . + Status: string @index(term) . + PrivateIpAddress: string @index(term) . + PublicIpAddress: string @index(term) . + Arn: string @index(term) . + Description: string @index(term) . + CidrBlock: string @index(term) . + _Vpc: uid @reverse . + _Instance: [uid] @reverse . + _AvailabilityZone: [uid] @reverse . + _TargetGroup: [uid] @reverse . + _LoadBalancer: [uid] @reverse . + _Image: uid @reverse . + _Subnet: [uid] @reverse . + _SecurityGroup: [uid] @reverse . + + InstanceId: string @index(term) . + InstanceType: string @index(term) . + EbsOptimized: bool . + Hypervisor: string @index(term) . + VirtualizationType: string @index(term) . + _KeyName: uid @reverse . + _InstanceProfile: uid @reverse . + + type Instance { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + InstanceId: string + InstanceType: string + State: string + EbsOptimized: bool + Hypervisor: string + VirtualizationType: string + PrivateIpAddress: string + PublicIpAddress: string + _KeyName: KeyPair + _AvailabilityZone: AvailabilityZone + _Vpc: Vpc + _InstanceProfile: InstanceProfile + _Image: Image + _Subnet: Subnet + } + + Encrypted: string @index(term) . + VolumeId: string @index(term) . + VolumeType: string @index(term) . + AvailabilityZone: string @index(term) . + Iops: int . + Size: int . + Device: string @index(term) . + DeleteOnTermination: bool . + + type Volume { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + VolumeId: string + DeleteOnTermination: bool + Device: string + Encrypted: bool + VolumeType: string + Size: int + Iops: int + State: string + Device: string + _AvailabilityZone: AvailabilityZone + _Instance: Instance + } + + PublicIp: string @index(term) . + Domain: string @index(term) . + AllocationId: string @index(term) . + _NatGateway: [uid] @reverse . + + type Address { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + PrivateIpAddress: string + PublicIp: string + Domain: string + AllocationId: string + _Instance: Instance + _NatGateway: NatGateway + } + + ZoneName: string @index(term) . + ZoneId: string @index(term) . + + type AvailabilityZone { + name: string + Service: string + Region: string + State: string + ZoneName: string + ZoneId: string + } + + KeyName: string @index(term) . + KeyFingerprint: string @index(term) . + + type KeyPair { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + State: string + KeyName: string + KeyFingerprint: string + } + + VpcId: string @index(term) . + + type Vpc { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + VpcId: string + CidrBlock: string + } + + InstanceProfileId: string @index(term) . + InstanceProfileName: string @index(term) . + + type InstanceProfile { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + InstanceProfileId: string + InstanceProfileName: string + Arn: string + } + + AutoScalingGroupArn: string @index(term) . + AutoScalingGroupName: string @index(term) . + MinSize: int . + MaxSize: int . + DesiredCapacity: int . + DefaultCooldown: int . + HealthCheckGracePeriod: int . + HealthCheckType: string @index(term) . + NewInstancesProtectedFromScaleIn: bool . + TerminationPolicy: string @index(term) . + _LaunchConfiguration: uid @reverse . + _LaunchTemplate: uid @reverse . + + type AutoScalingGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + AutoScalingGroupArn: string + AutoScalingGroupName: string + TerminationPolicy: string + MinSize: int + MaxSize: int + DesiredCapacity: int + DefaultCooldown: int + HealthCheckGracePeriod: int + HealthCheckType: string + NewInstancesProtectedFromScaleIn: bool + _Instance: Instance + _TargetGroup: TargetGroup + _AvailabilityZone: AvailabilityZone + _LoadBalancer: LoadBalancer + _LaunchConfiguration: LaunchConfiguration + _LaunchTemplate: LaunchTemplate + } + + LaunchConfigurationArn: string @index(term) . + LaunchConfigurationName: string @index(term) . + UserData: string @index(term) . + AssociatePublicIpAddress: bool . + + type LaunchConfiguration { + name: string + Service: string + Region: string + OwnerId: string + LaunchConfigurationArn: string + LaunchConfigurationName: string + UserData: string + AssociatePublicIpAddress: bool + InstanceType: string + EbsOptimized: bool + _KeyName: KeyPair + _InstanceProfile: InstanceProfile + _Image: Image + _SecurityGroup: SecurityGroup + } + + LaunchTemplateId: string @index(term) . + LaunchTemplateName: string @index(term) . + LatestVersionNumber: int . + DefaultVersionNumber: int . + CreatedBy: string @index(term) . + + type LaunchTemplate { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + LaunchTemplateId: string + LaunchTemplateName: string + LatestVersionNumber: int + DefaultVersionNumber: int + CreatedBy: string + } + + TargetGroupArn: string @index(term) . + TargetGroupName: string @index(term) . + TargetType: string @index(term) . + HealthCheckPath: string @index(term) . + HealthCheckPort: string @index(term) . + HealthyThresholdCount: int . + Port: int . + UnhealthyThresholdCount: int . + Protocol: string @index(term) . + + type TargetGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + TargetGroupArn: string + TargetGroupName: string + TargetType: string + HealthCheckPath: string + HealthCheckPort: string + HealthyThresholdCount: int + Port: int + UnhealthyThresholdCount: int + Protocol: string + _Vpc: Vpc + _LoadBalancer: LoadBalancer + } + + LoadBalancerName: string @index(term) . + LoadBalancerArn: string @index(term) . + CanonicalHostedZoneNameId: string @index(term) . + CanonicalHostedZoneId: string @index(term) . + CanonicalHostedZoneName: string @index(term) . + DNSName: string @index(term) . + Scheme: string @index(term) . + LoadBalancerType: string @index(term) . + IPAddressType: string @index(term) . + + type LoadBalancer { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + LoadBalancerName: string + LoadBalancerArn: string + CanonicalHostedZoneNameId: string + CanonicalHostedZoneId: string + CanonicalHostedZoneName: string + DNSName: string + Scheme: string + LoadBalancerType: string + IPAddressType: string + State: string + _Vpc: Vpc + _Instance: Instance + _AvailabilityZone: AvailabilityZone + } + + SnapshotId: string @index(term) . + VolumeSize: int . + Encrypted: bool . + Progress: string @index(term) . + _Volume: uid @reverse . + + type Snapshot { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + VpcId: string + SnapshotId: string + Description: string + VolumeSize: int + Encrypted: bool + Progress: string + _Volume: Volume + } + + ImageId: string @index(term) . + VirtualizationType: string @index(term) . + Hypervisor: string @index(term) . + EnaSupport: bool . + SriovNetSupport: string @index(term) . + State: string @index(term) . + Architecture: string @index(term) . + ImageLocation: string @index(term) . + RootDeviceType: string @index(term) . + RootDeviceName: string @index(term) . + Public: bool . + ImageType: string @index(term) . + + type Image { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + ImageId: string + VirtualizationType: string + Hypervisor: string + EnaSupport: bool + SriovNetSupport: string + State: string + Architecture: string + ImageLocation: string + RootDeviceType: string + RootDeviceName: string + Public: bool + ImageType: string + } + + SubnetId: string @index(term) . + MapPublicIPOnLaunch: bool . + DefaultForAz: bool . + AssignIPv6AddressOnCreation: bool . + AvailableIPAddressCount: string @index(term) . + PortTcp: string @index(term) . + PortUdp: string @index(term) . + + type Subnet { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + SubnetId: string + MapPublicIPOnLaunch: bool + DefaultForAz: bool + AssignIPv6AddressOnCreation: bool + AvailableIPAddressCount: string + State: string + CidrBlock: string + _Vpc: Vpc + _AvailibilityZone: AvailibilityZone + } + + GroupId: string @index(term) . + GroupName: string @index(term) . + PortTcp: string . + _Cidr: [uid] @reverse . + + type SecurityGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + GroupId: string + GroupName: string + PortTcp: string + _Vpc: Vpc + _SecurityGroup: SecurityGroup + _Cidr: Cidr + } + + type Cidr { + name: string + Service: string + PortTcp: string + } + + DbInstanceArn: string @index(term) . + DbInstanceIdentifier: string @index(term) . + DBName: string @index(term) . + DbInstanceStatus: string @index(term) . + DbInstanceClass: string @index(term) . + DbiResourceID: string @index(term) . + CACertificateIdentifier: string @index(term) . + PubliclyAccessible: bool . + MasterUsername: string @index(term) . + LicenseModel: string @index(term) . + CopyTagsToSnapshot: bool . + Engine: string @index(term) . + EngineVersion: string @index(term) . + Endpoint: string @index(term) . + Port: int . + MultiAZ: bool . + AutoMinorVersionUpgrade: bool . + IAMDatabaseAuthenticationEnabled: bool . + DeletionProtection: bool . + PreferredBackupWindow: string @index(term) . + PreferredMaintenanceWindow: string @index(term) . + PromotionTier: int . + AllocatedStorage: int . + StorageType: string @index(term) . + StorageEncrypted: bool . + BackupRetentionPeriod: int . + IsClusterWriter: bool . + _DbSubnetGroup: [uid] @reverse . + _DbParameterGroup: uid @reverse . + _OptionGroup: uid @reverse . + + type DbInstance { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + DbInstanceArn: string + DbInstanceIdentifier: string + DBName: string + DbInstanceStatus: string + DbInstanceClass: string + DbiResourceID: string + CACertificateIdentifier: string + PubliclyAccessible: bool + MasterUsername: string + LicenseModel: string + CopyTagsToSnapshot: bool + Engine: string + EngineVersion: string + Endpoint: string + Port: int + MultiAZ: bool + AutoMinorVersionUpgrade: bool + IAMDatabaseAuthenticationEnabled: bool + DeletionProtection: bool + PreferredBackupWindow: string + PreferredMaintenanceWindow: string + PromotionTier: int + AllocatedStorage: int + StorageType: string + StorageEncrypted: bool + BackupRetentionPeriod: int + _AvailabilityZone: AvailabilityZone + _SecondaryAvailabilityZone: AvailabilityZone + _SecurityGroup: SecurityGroup + _Vpc: Vpc + _DbSubnetGroup: DbSubnetGroup + _DbParameterGroup: DbParameterGroup + _OptionGroup: OptionGroup + } + + DbClusterArn: string @index(term) . + DbClusterIdentifier: string @index(term) . + DbClusterResourceID: string @index(term) . + HostedZoneID: string @index(term) . + HTTPEndpointEnabled: bool . + EngineMode: string @index(term) . + ReaderEndpoint: string @index(term) . + _DbClusterMember: [uid] @reverse . + _ReadReplica: [uid] @reverse . + + type DbCluster { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + DbClusterArn: string + DbClusterIdentifier: string + DbClusterResourceID: string + HostedZoneID: string + HTTPEndpointEnabled: bool + DBName: string + DbiResourceID: string + MasterUsername: string + Engine: string + EngineMode: string + EngineVersion: string + Endpoint: string + ReaderEndpoint: string + Port: int + MultiAZ: bool + Status: string + IAMDatabaseAuthenticationEnabled: bool + DeletionProtection: bool + PreferredBackupWindow: string + PreferredMaintenanceWindow: string + AllocatedStorage: int + StorageType: string + StorageEncrypted: bool + BackupRetentionPeriod: int + _AvailabilityZone: AvailabilityZone + _SecurityGroup: SecurityGroup + _DbClusterMember: DbInstance + _ReadReplica: DbInstance + _DbClusterParameterGroup: DbClusterParameterGroup + _DbSubnetGroup: DbSubnetGroup + } + + OptionGroupArn: string @index(term) . + OptionGroupName: string @index(term) . + MajorEngineVersion: string @index(term) . + EngineName: string @index(term) . + AllowsVpcAndNonVpcInstanceMemberships: bool . + + type OptionGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + OptionGroupArn: string + OptionGroupName: string + MajorEngineVersion: string + EngineName: string + AllowsVpcAndNonVpcInstanceMemberships: bool + } + + DbParameterGroupArn: string @index(term) . + DbParameterGroupName: string @index(term) . + DbParameterGroupFamily: string @index(term) . + + type DbParameterGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + DbParameterGroupArn: string + DbParameterGroupName: string + DbParameterGroupFamily: string + Description: string + } + + DbClusterParameterGroupArn: string @index(term) . + DbClusterParameterGroupName: string @index(term) . + + type DbClusterParameterGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + DbClusterParameterGroupArn: string + DbClusterParameterGroupName: string + DbParameterGroupFamily: string + DbClusterscription: string + } + + DbSubnetGroupArn: string @index(term) . + DbSubnetGroupName: string @index(term) . + SubnetGroupStatus: string @index(term) . + + type DbSubnetGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + DbSubnetGroupArn: string + DbSubnetGroupName: string + SubnetGroupStatus: string + Description: string + } + + CacheClusterId: string @index(term) . + AuthTokenEnabled: bool . + NumCacheNodes: int . + TransitEncryptionEnabled: bool . + CacheNodeType: string @index(term) . + _CacheSubnetGroup: uid @reverse . + + type CacheCluster { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + CacheClusterId: string + AuthTokenEnabled: bool + NumCacheNodes: int + TransitEncryptionEnabled: bool + CacheNodeType: string + Engine: string + EngineVersion: string + Endpoint: string + Port: string + EngineVersion: string + AutoMinorVersionUpgrade: bool + Status: string + _AvailabilityZone: AvailabilityZone + _SecurityGroup: SecurityGroup + _Subnet: Subnet + } + + CacheSubnetGroupName: string @index(term) . + + type CacheSubnetGroup { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + CacheSubnetGroupName: string + Description: string + _Vpc: Vpc + _Subnet: Subnet + } + + VpcPeeringConnectionId: string @index(term) . + _AccepterVpc: uid @reverse . + _RequesterVpc: uid @reverse . + + type VpcPeeringConnection { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + VpcPeeringConnectionId: string + Status: string + _AccepterVpc: Vpc + _RequesterVpc: Vpc + } + + NatGatewayId: string @index(term) . + + type NatGateway { + name: string + Service: string + Region: string + OwnerId: string + OwnerName: string + NatGatewayId: string + PublicIpAddress: string + PrivateIpAddress: string + State: string + _Vpc: Vpc + _Subnet: Subnet + } +` + return s +} diff --git a/securitygroups.go b/securitygroups.go new file mode 100644 index 0000000..84c0bac --- /dev/null +++ b/securitygroups.go @@ -0,0 +1,194 @@ +package main + +import ( + "encoding/json" + "log" + "strconv" + "strings" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type securityGroupList struct { + *ec2.DescribeSecurityGroupsOutput +} +type securityGroupNodes []securityGroupNode +type cidrNodes []cidrNode + +type securityGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + GroupID string `json:"GroupId,omitempty"` + Description string `json:"Description,omitempty"` + GroupName string `json:"GroupName,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + SecurityGroup securityGroupNodes `json:"_SecurityGroup,omitempty"` + SecurityGroupPortTCP string `json:"_SecurityGroup|PortTcp,omitempty"` + Cidr cidrNodes `json:"_Cidr,omitempty"` + // SecurityGroupPortUDP string `json:"_SecurityGroup|PortUdp,omitempty"` +} + +type cidrNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + CidrPortTCP string `json:"_Cidr|PortTcp,omitempty"` +} + +func (c *connector) listSecurityGroups() securityGroupList { + defer c.waitGroup.Done() + + log.Println("List securityGroups") + response, err := ec2.New(c.awsSession).DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return securityGroupList{response} +} + +func (list securityGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.SecurityGroups) == 0 { + return + } + log.Println("Add SecurityGroup Nodes") + a := make(securityGroupNodes, 0, len(list.SecurityGroups)) + z := make(cidrNodes, 0) + x, y := make(map[string]string), make(map[string]string) + + for _, i := range list.SecurityGroups { + var b securityGroupNode + b.Service = "ec2" + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Type = []string{"SecurityGroup"} + b.Name = *i.GroupName + b.GroupID = *i.GroupId + b.GroupName = *i.GroupName + b.Description = *i.Description + for _, j := range i.IpPermissions { + for _, k := range j.UserIdGroupPairs { + var p string + if *j.IpProtocol == "tcp" { + if *j.FromPort == *j.ToPort { + p = strconv.FormatInt(*j.FromPort, 10) + } else { + p = strconv.FormatInt(*j.FromPort, 10) + "-" + strconv.FormatInt(*j.ToPort, 10) + } + } + if *j.IpProtocol == "-1" { + p = "0-65535" + } + x[*k.GroupId] += *i.GroupId + "#" + p + "!" + } + for _, k := range j.IpRanges { + var p string + if *j.IpProtocol == "tcp" { + if *j.FromPort == *j.ToPort { + p = strconv.FormatInt(*j.FromPort, 10) + } else { + p = strconv.FormatInt(*j.FromPort, 10) + "-" + strconv.FormatInt(*j.ToPort, 10) + } + } + if *j.IpProtocol == "-1" { + p = "0-65535" + } + y[*k.CidrIp] += *i.GroupId + "#" + p + "!" + } + } + a = append(a, b) + } + c.dgraphAddNodes(a) + + if len(y) != 0 { + for i := range y { + z = append(z, cidrNode{ + Service: "ec2", + Type: []string{"Cidr"}, + Name: i, + }) + } + c.dgraphAddNodes(z) + } + + m := make(map[string]securityGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("SecurityGroup"), &m) + for _, i := range m["list"] { + n[i.GroupID] = i.UID + } + c.ressources["SecurityGroups"] = n + + l := make(map[string]string) + for _, i := range m["list"] { + l[i.GroupID] = i.UID + "%" + x[i.GroupID] + } + c.ressources["SecurityGroupEdges"] = l + + o := make(map[string]cidrNodes) + p := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Cidr"), &o) + for _, i := range o["list"] { + p[i.Name] = i.UID + "%" + y[i.Name] + } + c.ressources["Cidr"] = p +} + +func (list securityGroupList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.SecurityGroups) == 0 { + return + } + log.Println("Add SecurityGroup Edges") + a := securityGroupNodes{} + for _, i := range list.SecurityGroups { + b := securityGroupNode{ + UID: c.ressources["SecurityGroups"][*i.GroupId], + } + if i.VpcId != nil { + b.Vpc = vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]} + } + for _, j := range i.IpPermissions { + for _, k := range j.UserIdGroupPairs { + s := strings.Split(c.ressources["SecurityGroupEdges"][*k.GroupId], "%") + if len(s) >= 2 { + t := strings.Split(s[1], "!") + var u string + for _, l := range t { + m := strings.Split(l, "#") + if m[0] == *i.GroupId { + u += m[1] + "," + } + } + b.SecurityGroup = append(b.SecurityGroup, securityGroupNode{UID: s[0], SecurityGroupPortTCP: u}) + } + } + for _, k := range j.IpRanges { + s := strings.Split(c.ressources["Cidr"][*k.CidrIp], "%") + if len(s) >= 2 { + t := strings.Split(s[1], "!") + var u string + for _, l := range t { + m := strings.Split(l, "#") + if m[0] == *i.GroupId { + u += m[1] + "," + } + } + b.Cidr = append(b.Cidr, cidrNode{UID: s[0], CidrPortTCP: u}) + } + } + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/snapshots.go b/snapshots.go new file mode 100644 index 0000000..c34d83c --- /dev/null +++ b/snapshots.go @@ -0,0 +1,105 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" +) + +type snapshotList struct{ *ec2.DescribeSnapshotsOutput } +type snapshotNodes []snapshotNode + +type snapshotNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + SnapshotID string `json:"SnapshotId,omitempty"` + Description string `json:"Description,omitempty"` + VolumeSize int64 `json:"VolumeSize,omitempty"` + Encrypted bool `json:"Encrypted,omitempty"` + Progress string `json:"Progress,omitempty"` + Volume volumeNode `json:"_Volume,omitempty"` +} + +func (c *connector) listSnapshots() snapshotList { + defer c.waitGroup.Done() + + log.Println("List Snapshots") + response, err := ec2.New(c.awsSession).DescribeSnapshots(&ec2.DescribeSnapshotsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("owner-id"), + Values: []*string{aws.String(c.awsAccountID)}, + }, + }, + }) + if err != nil { + log.Fatal(err) + } + return snapshotList{response} +} + +func (list snapshotList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Snapshots) == 0 { + return + } + log.Println("Add Snapshot Nodes") + a := make(snapshotNodes, 0, len(list.Snapshots)) + + for _, i := range list.Snapshots { + var b snapshotNode + b.Service = "ec2" + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Type = []string{"Snapshot"} + b.Name = *i.SnapshotId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.SnapshotID = *i.SnapshotId + b.Description = *i.Description + b.VolumeSize = *i.VolumeSize + b.Encrypted = *i.Encrypted + b.Progress = *i.Progress + a = append(a, b) + } + + c.dgraphAddNodes(a) + + m := make(map[string]snapshotNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Snapshot"), &m) + for _, i := range m["list"] { + n[i.SnapshotID] = i.UID + } + c.ressources["Snapshots"] = n +} + +func (list snapshotList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.Snapshots) == 0 { + return + } + log.Println("Add Snapshot Edges") + a := snapshotNodes{} + for _, i := range list.Snapshots { + b := snapshotNode{ + UID: c.ressources["Snapshots"][*i.SnapshotId], + Volume: volumeNode{UID: c.ressources["Volumes"][*i.VolumeId]}, + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/subnets.go b/subnets.go new file mode 100644 index 0000000..b452b67 --- /dev/null +++ b/subnets.go @@ -0,0 +1,104 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type subnetList struct { + *ec2.DescribeSubnetsOutput +} +type subnetNodes []subnetNode + +type subnetNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Service string `json:"Service,omitempty"` + SubnetID string `json:"SubnetId,omitempty"` + CidrBlock string `json:"CidrBlock,omitempty"` + MapPublicIPOnLaunch bool `json:"MapPublicIpOnLaunch,omitempty"` + DefaultForAz bool `json:"MapPuDefaultForAzblicIpOnLaunch,omitempty"` + State string `json:"State,omitempty"` + AssignIPv6AddressOnCreation bool `json:"AssignIpv6AddressOnCreation,omitempty"` + AvailableIPAddressCount int64 `json:"AvailableIpAddressCount,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` +} + +func (c *connector) listSubnets() subnetList { + defer c.waitGroup.Done() + + log.Println("List Subnets") + response, err := ec2.New(c.awsSession).DescribeSubnets(&ec2.DescribeSubnetsInput{}) + if err != nil { + log.Fatal(err) + } + return subnetList{response} +} + +func (list subnetList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Subnets) == 0 { + return + } + log.Println("Add Subnet Nodes") + a := make(subnetNodes, 0, len(list.Subnets)) + + for _, i := range list.Subnets { + var b subnetNode + b.Service = "ec2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"Subnet"} + b.Name = *i.SubnetId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.SubnetID = *i.SubnetId + b.MapPublicIPOnLaunch = *i.MapPublicIpOnLaunch + b.AvailableIPAddressCount = *i.AvailableIpAddressCount + b.DefaultForAz = *i.DefaultForAz + b.State = *i.State + b.CidrBlock = *i.CidrBlock + b.AssignIPv6AddressOnCreation = *i.AssignIpv6AddressOnCreation + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]subnetNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Subnet"), &m) + for _, i := range m["list"] { + n[i.SubnetID] = i.UID + } + c.ressources["Subnets"] = n +} + +func (list subnetList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.Subnets) == 0 { + return + } + log.Println("Add Subnet Edges") + a := subnetNodes{} + for _, i := range list.Subnets { + b := subnetNode{ + UID: c.ressources["Subnets"][*i.SubnetId], + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]}, + AvailabilityZone: availabilityZoneNodes{availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.AvailabilityZone]}}, + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/targetgroups.go b/targetgroups.go new file mode 100644 index 0000000..21a2cef --- /dev/null +++ b/targetgroups.go @@ -0,0 +1,109 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/elbv2" +) + +type targetGroupList struct { + *elbv2.DescribeTargetGroupsOutput +} +type targetGroupNodes []targetGroupNode + +type targetGroupNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Service string `json:"Service,omitempty"` + TargetGroupArn string `json:"TargetGroupArn,omitempty"` + TargetGroupName string `json:"TargetGroupName,omitempty"` + TargetType string `json:"TargetType,omitempty"` + HealthCheckPath string `json:"HealthCheckPath,omitempty"` + HealthCheckProtocol string `json:"HealthCheckProtocol,omitempty"` + HealthCheckPort string `json:"HealthCheckPort,omitempty"` + HealthyThresholdCount int64 `json:"HealthCheckIntervalSeconds,omitempty"` + Port int64 `json:"Port,omitempty"` + UnhealthyThresholdCount int64 `json:"UnhealthyThresholdCount,omitempty"` + Protocol string `json:"Protocol,omitempty"` + Vpc vpcNode `json:"_Vpc,omitempty"` + LoadBalancer loadBalancerNodes `json:"_LoadBalancer,omitempty"` +} + +func (c *connector) listTargetGroups() targetGroupList { + defer c.waitGroup.Done() + + log.Println("List TargetGroups") + response, err := elbv2.New(c.awsSession).DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{}) + if err != nil { + log.Fatal(err) + } + return targetGroupList{response} +} + +func (list targetGroupList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.TargetGroups) == 0 { + return + } + log.Println("Add TargetGroup Nodes") + a := make(targetGroupNodes, 0, len(list.TargetGroups)) + + for _, i := range list.TargetGroups { + var b targetGroupNode + b.Service = "elbv2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"TargetGroup"} + b.Name = *i.TargetGroupName + b.TargetGroupName = *i.TargetGroupName + b.TargetGroupArn = *i.TargetGroupArn + b.TargetType = *i.TargetType + if i.HealthCheckPath != nil { + b.HealthCheckPath = *i.HealthCheckPath + b.HealthCheckProtocol = *i.HealthCheckProtocol + b.HealthCheckPort = *i.HealthCheckPort + b.HealthyThresholdCount = *i.HealthyThresholdCount + b.UnhealthyThresholdCount = *i.UnhealthyThresholdCount + } + b.Port = *i.Port + b.Protocol = *i.Protocol + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]targetGroupNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("TargetGroup"), &m) + for _, i := range m["list"] { + n[i.TargetGroupArn] = i.UID + } + c.ressources["TargetGroups"] = n +} + +func (list targetGroupList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.TargetGroups) == 0 { + return + } + log.Println("Add TargetGroup Edges") + a := targetGroupNodes{} + for _, i := range list.TargetGroups { + b := targetGroupNode{ + UID: c.ressources["TargetGroups"][*i.TargetGroupArn], + Vpc: vpcNode{UID: c.ressources["Vpcs"][*i.VpcId]}, + } + for _, i := range i.LoadBalancerArns { + b.LoadBalancer = append(b.LoadBalancer, loadBalancerNode{UID: c.ressources["LoadBalancersV2"][*i]}) + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/volumes.go b/volumes.go new file mode 100644 index 0000000..984cae9 --- /dev/null +++ b/volumes.go @@ -0,0 +1,108 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type volumeList struct{ *ec2.DescribeVolumesOutput } +type volumeNodes []volumeNode + +type volumeNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + Encrypted bool `json:"Encrypted,omitempty"` + VolumeID string `json:"VolumeId,omitempty"` + VolumeType string `json:"VolumeType,omitempty"` + Size int64 `json:"Size,omitempty"` + Iops int64 `json:"Iops,omitempty"` + State string `json:"State,omitempty"` + Device string `json:"Device,omitempty"` + DeleteOnTermination bool `json:"DeleteOnTermination,omitempty"` + AvailabilityZone availabilityZoneNodes `json:"_AvailabilityZone,omitempty"` + Instance instanceNodes `json:"_Instance,omitempty"` +} + +func (c *connector) listVolumes() volumeList { + defer c.waitGroup.Done() + + log.Println("List Volumes") + response, err := ec2.New(c.awsSession).DescribeVolumes(&ec2.DescribeVolumesInput{}) + if err != nil { + log.Fatal(err) + } + return volumeList{response} +} + +func (list volumeList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Volumes) == 0 { + return + } + log.Println("Add Volume Nodes") + a := make(volumeNodes, 0, len(list.Volumes)) + + for _, i := range list.Volumes { + var b volumeNode + b.Service = "ec2" + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Region = c.awsRegion + b.Type = []string{"Volume"} + b.Name = *i.VolumeId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.VolumeID = *i.VolumeId + b.VolumeType = *i.VolumeType + b.State = *i.State + b.Size = *i.Size + if *i.VolumeType == "gp2" || *i.VolumeType == "iops" { + b.Iops = *i.Iops + } + if len(i.Attachments) != 0 { + b.Device = *i.Attachments[0].Device + } + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]volumeNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Volume"), &m) + for _, i := range m["list"] { + n[i.VolumeID] = i.UID + } + c.ressources["Volumes"] = n +} + +func (list volumeList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.Volumes) == 0 { + return + } + log.Println("Add Volume Edges") + a := volumeNodes{} + for _, i := range list.Volumes { + b := volumeNode{ + UID: c.ressources["Volumes"][*i.VolumeId], + AvailabilityZone: availabilityZoneNodes{availabilityZoneNode{UID: c.ressources["AvailabilityZones"][*i.AvailabilityZone]}}, + } + if *i.State == "in-use" { + b.Instance = instanceNodes{instanceNode{UID: c.ressources["Instances"][*i.Attachments[0].InstanceId]}} + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/vpcpeeringconnections.go b/vpcpeeringconnections.go new file mode 100644 index 0000000..0a9c33a --- /dev/null +++ b/vpcpeeringconnections.go @@ -0,0 +1,94 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type vpcPeeringConnectionList struct { + *ec2.DescribeVpcPeeringConnectionsOutput +} +type vpcPeeringConnectionNodes []vpcPeeringConnectionNode + +type vpcPeeringConnectionNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + Region string `json:"Region,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + OwnerID string `json:"OwnerId,omitempty"` + Service string `json:"Service,omitempty"` + VpcPeeringConnectionID string `json:"VpcPeeringConnectionId,omitempty"` + Status string `json:"Status,omitempty"` + AccepterVpc vpcNode `json:"_AccepterVpc,omitempty"` + RequesterVpc vpcNode `json:"_RequesterVpc,omitempty"` +} + +func (c *connector) listVpcPeeringConnections() vpcPeeringConnectionList { + defer c.waitGroup.Done() + + log.Println("List VpcPeeringConnections") + response, err := ec2.New(c.awsSession).DescribeVpcPeeringConnections(&ec2.DescribeVpcPeeringConnectionsInput{}) + if err != nil { + log.Fatal(err) + } + return vpcPeeringConnectionList{response} +} + +func (list vpcPeeringConnectionList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.VpcPeeringConnections) == 0 { + return + } + log.Println("Add VpcPeeringConnection Nodes") + a := make(vpcPeeringConnectionNodes, 0, len(list.VpcPeeringConnections)) + + for _, i := range list.VpcPeeringConnections { + var b vpcPeeringConnectionNode + b.Service = "ec2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"VpcPeeringConnection"} + b.Name = *i.VpcPeeringConnectionId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.VpcPeeringConnectionID = *i.VpcPeeringConnectionId + b.Status = *i.Status.Code + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]vpcPeeringConnectionNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("VpcPeeringConnection"), &m) + for _, i := range m["list"] { + n[i.VpcPeeringConnectionID] = i.UID + } + c.ressources["VpcPeeringConnections"] = n +} + +func (list vpcPeeringConnectionList) addEdges(c *connector) { + defer c.waitGroup.Done() + + if len(list.VpcPeeringConnections) == 0 { + return + } + log.Println("Add VpcPeeringConnection Edges") + a := vpcPeeringConnectionNodes{} + for _, i := range list.VpcPeeringConnections { + b := vpcPeeringConnectionNode{ + UID: c.ressources["VpcPeeringConnections"][*i.VpcPeeringConnectionId], + AccepterVpc: vpcNode{UID: c.ressources["Vpcs"][*i.AccepterVpcInfo.VpcId]}, + RequesterVpc: vpcNode{UID: c.ressources["Vpcs"][*i.RequesterVpcInfo.VpcId]}, + } + a = append(a, b) + } + c.dgraphAddNodes(a) +} diff --git a/vpcs.go b/vpcs.go new file mode 100644 index 0000000..87c7731 --- /dev/null +++ b/vpcs.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/json" + "log" + + "github.com/aws/aws-sdk-go/service/ec2" +) + +type vpcList struct { + *ec2.DescribeVpcsOutput +} +type vpcNodes []vpcNode + +type vpcNode struct { + UID string `json:"uid,omitempty"` + Type []string `json:"dgraph.type,omitempty"` + Name string `json:"name,omitempty"` // This field is only for Ratel Viz + OwnerID string `json:"OwnerId,omitempty"` + OwnerName string `json:"OwnerName,omitempty"` + Region string `json:"Region,omitempty"` + Service string `json:"Service,omitempty"` + VpcID string `json:"VpcId,omitempty"` + CidrBlock string `json:"CidrBlock,omitempty"` +} + +func (c *connector) listVpcs() vpcList { + defer c.waitGroup.Done() + + log.Println("List VPCs") + response, err := ec2.New(c.awsSession).DescribeVpcs(&ec2.DescribeVpcsInput{}) + if err != nil { + log.Fatal(err) + } + return vpcList{response} +} + +func (list vpcList) addNodes(c *connector) { + defer c.waitGroup.Done() + + if len(list.Vpcs) == 0 { + return + } + log.Println("Add VPC Nodes") + a := make(vpcNodes, 0, len(list.Vpcs)) + + for _, i := range list.Vpcs { + var b vpcNode + b.Service = "ec2" + b.Region = c.awsRegion + b.OwnerID = c.awsAccountID + b.OwnerName = c.awsAccountName + b.Type = []string{"Vpc"} + b.Name = *i.VpcId + for _, tag := range i.Tags { + if *tag.Key == "Name" { + b.Name = *tag.Value + } + } + b.VpcID = *i.VpcId + a = append(a, b) + } + c.dgraphAddNodes(a) + + m := make(map[string]vpcNodes) + n := make(map[string]string) + json.Unmarshal(c.dgraphQuery("Vpc"), &m) + for _, i := range m["list"] { + n[i.VpcID] = i.UID + } + c.ressources["Vpcs"] = n +}