├── .github ├── e2eapply.tape ├── e2eplan.tape └── workflows │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .goreleaser.yaml ├── .terraform.lock.hcl ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── banner_dark.svg ├── banner_light.svg ├── blastRadiusItems.png ├── chainLink.png ├── change.png ├── change.svg ├── changed.png ├── changed.svg ├── created.png ├── created.svg ├── deleted.png ├── deleted.svg ├── edge.png ├── edge.svg ├── high.png ├── high.svg ├── item.png ├── item.svg ├── link.svg ├── logo.png ├── low.png ├── low.svg ├── medium.png ├── medium.svg ├── open_in_overmind.svg ├── replaced.png ├── replaced.svg ├── risks.png ├── risks.svg ├── unchanged.svg └── unmapped.svg ├── auth ├── auth.go ├── auth_client.go ├── auth_test.go ├── middleware.go ├── middleware_test.go ├── nats.go ├── nats_test.go └── tracing.go ├── aws-source ├── .deadcode-ignore ├── acceptance │ └── nats-server.conf ├── adapterhelpers │ ├── always_get_source.go │ ├── always_get_source_test.go │ ├── describe_source.go │ ├── describe_source_test.go │ ├── get_list_adapter_v2.go │ ├── get_list_adapter_v2_test.go │ ├── get_list_source.go │ ├── get_list_source_test.go │ ├── shared_tests.go │ ├── sources.go │ ├── util.go │ └── util_test.go ├── adapters │ ├── apigateway-api-key.go │ ├── apigateway-api-key_test.go │ ├── apigateway-authorizer.go │ ├── apigateway-authorizer_test.go │ ├── apigateway-deployment.go │ ├── apigateway-deployment_test.go │ ├── apigateway-domain-name.go │ ├── apigateway-domain-name_test.go │ ├── apigateway-integration.go │ ├── apigateway-integration_test.go │ ├── apigateway-method-response.go │ ├── apigateway-method-response_test.go │ ├── apigateway-method.go │ ├── apigateway-method_test.go │ ├── apigateway-model.go │ ├── apigateway-model_test.go │ ├── apigateway-resource.go │ ├── apigateway-resource_test.go │ ├── apigateway-rest-api.go │ ├── apigateway-rest-api_test.go │ ├── apigateway-stage.go │ ├── apigateway-stage_test.go │ ├── autoscaling-auto-scaling-group.go │ ├── autoscaling-auto-scaling-group_test.go │ ├── cloudfront-cache-policy.go │ ├── cloudfront-cache-policy_test.go │ ├── cloudfront-continuous-deployment-policy.go │ ├── cloudfront-continuous-deployment-policy_test.go │ ├── cloudfront-distribution.go │ ├── cloudfront-distribution_test.go │ ├── cloudfront-function.go │ ├── cloudfront-function_test.go │ ├── cloudfront-key-group.go │ ├── cloudfront-key-group_test.go │ ├── cloudfront-origin-access-control.go │ ├── cloudfront-origin-access-control_test.go │ ├── cloudfront-origin-request-policy.go │ ├── cloudfront-origin-request-policy_test.go │ ├── cloudfront-realtime-log-config.go │ ├── cloudfront-realtime-log-config_test.go │ ├── cloudfront-response-headers-policy.go │ ├── cloudfront-response-headers-policy_test.go │ ├── cloudfront-streaming-distribution.go │ ├── cloudfront-streaming-distribution_test.go │ ├── cloudfront.go │ ├── cloudfront_test.go │ ├── cloudwatch-alarm.go │ ├── cloudwatch-alarm_test.go │ ├── cloudwatch_metric_links.go │ ├── cloudwatch_metric_links_test.go │ ├── directconnect-connection.go │ ├── directconnect-connection_test.go │ ├── directconnect-customer-metadata.go │ ├── directconnect-customer-metadata_test.go │ ├── directconnect-direct-connect-gateway-association-proposal.go │ ├── directconnect-direct-connect-gateway-association-proposal_test.go │ ├── directconnect-direct-connect-gateway-association.go │ ├── directconnect-direct-connect-gateway-association_test.go │ ├── directconnect-direct-connect-gateway-attachment.go │ ├── directconnect-direct-connect-gateway-attachment_test.go │ ├── directconnect-direct-connect-gateway.go │ ├── directconnect-direct-connect-gateway_test.go │ ├── directconnect-hosted-connection.go │ ├── directconnect-hosted-connection_test.go │ ├── directconnect-interconnect.go │ ├── directconnect-interconnect_test.go │ ├── directconnect-lag.go │ ├── directconnect-lag_test.go │ ├── directconnect-location.go │ ├── directconnect-location_test.go │ ├── directconnect-router-configuration.go │ ├── directconnect-router-configuration_test.go │ ├── directconnect-virtual-gateway.go │ ├── directconnect-virtual-gateway_test.go │ ├── directconnect-virtual-interface.go │ ├── directconnect-virtual-interface_test.go │ ├── directconnect.go │ ├── directconnect_test.go │ ├── dynamodb-backup.go │ ├── dynamodb-backup_test.go │ ├── dynamodb-table.go │ ├── dynamodb-table_test.go │ ├── dynamodb.go │ ├── dynamodb_test.go │ ├── ec2-address.go │ ├── ec2-address_test.go │ ├── ec2-capacity-reservation-fleet.go │ ├── ec2-capacity-reservation-fleet_test.go │ ├── ec2-capacity-reservation.go │ ├── ec2-capacity-reservation_test.go │ ├── ec2-egress-only-internet-gateway.go │ ├── ec2-egress-only-internet-gateway_test.go │ ├── ec2-iam-instance-profile-association.go │ ├── ec2-iam-instance-profile-association_test.go │ ├── ec2-image.go │ ├── ec2-image_test.go │ ├── ec2-instance-event-window.go │ ├── ec2-instance-event-window_test.go │ ├── ec2-instance-status.go │ ├── ec2-instance-status_test.go │ ├── ec2-instance.go │ ├── ec2-instance_test.go │ ├── ec2-internet-gateway.go │ ├── ec2-internet-gateway_test.go │ ├── ec2-key-pair.go │ ├── ec2-key-pair_test.go │ ├── ec2-launch-template-version.go │ ├── ec2-launch-template-version_test.go │ ├── ec2-launch-template.go │ ├── ec2-launch-template_test.go │ ├── ec2-nat-gateway.go │ ├── ec2-nat-gateway_test.go │ ├── ec2-network-acl.go │ ├── ec2-network-acl_test.go │ ├── ec2-network-interface-permission.go │ ├── ec2-network-interface-permission_test.go │ ├── ec2-network-interface.go │ ├── ec2-network-interface_test.go │ ├── ec2-placement-group.go │ ├── ec2-placement-group_test.go │ ├── ec2-reserved-instance.go │ ├── ec2-reserved-instance_test.go │ ├── ec2-route-table.go │ ├── ec2-route-table_test.go │ ├── ec2-security-group-rule.go │ ├── ec2-security-group-rule_test.go │ ├── ec2-security-group.go │ ├── ec2-security-group_test.go │ ├── ec2-snapshot.go │ ├── ec2-snapshot_test.go │ ├── ec2-subnet.go │ ├── ec2-subnet_test.go │ ├── ec2-volume-status.go │ ├── ec2-volume-status_test.go │ ├── ec2-volume.go │ ├── ec2-volume_test.go │ ├── ec2-vpc-endpoint.go │ ├── ec2-vpc-endpoint_test.go │ ├── ec2-vpc-peering-connection.go │ ├── ec2-vpc-peering-connection_test.go │ ├── ec2-vpc.go │ ├── ec2-vpc_test.go │ ├── ec2.go │ ├── ec2_test.go │ ├── ecs-capacity-provider.go │ ├── ecs-capacity-provider_test.go │ ├── ecs-cluster.go │ ├── ecs-cluster_test.go │ ├── ecs-container-instance.go │ ├── ecs-container-instance_test.go │ ├── ecs-service.go │ ├── ecs-service_test.go │ ├── ecs-task-definition.go │ ├── ecs-task-definition_test.go │ ├── ecs-task.go │ ├── ecs-task_test.go │ ├── ecs.go │ ├── ecs_test.go │ ├── efs-access-point.go │ ├── efs-access-point_test.go │ ├── efs-backup-policy.go │ ├── efs-backup-policy_test.go │ ├── efs-file-system.go │ ├── efs-file-system_test.go │ ├── efs-mount-target.go │ ├── efs-mount-target_test.go │ ├── efs-replication-configuration.go │ ├── efs-replication-configuration_test.go │ ├── efs.go │ ├── efs_test.go │ ├── eks-addon.go │ ├── eks-addon_test.go │ ├── eks-cluster.go │ ├── eks-cluster_test.go │ ├── eks-fargate-profile.go │ ├── eks-fargate-profile_test.go │ ├── eks-nodegroup.go │ ├── eks-nodegroup_test.go │ ├── eks.go │ ├── eks_test.go │ ├── elb-instance-health.go │ ├── elb-instance-health_test.go │ ├── elb-load-balancer.go │ ├── elb-load-balancer_test.go │ ├── elbv2-listener.go │ ├── elbv2-listener_test.go │ ├── elbv2-load-balancer.go │ ├── elbv2-load-balancer_test.go │ ├── elbv2-rule.go │ ├── elbv2-rule_test.go │ ├── elbv2-target-group.go │ ├── elbv2-target-group_test.go │ ├── elbv2-target-health.go │ ├── elbv2-target-health_test.go │ ├── elbv2.go │ ├── elbv2_test.go │ ├── iam-group.go │ ├── iam-group_test.go │ ├── iam-instance-profile.go │ ├── iam-instance-profile_test.go │ ├── iam-policy.go │ ├── iam-policy_test.go │ ├── iam-role.go │ ├── iam-role_test.go │ ├── iam-user.go │ ├── iam-user_test.go │ ├── iam.go │ ├── iam_test.go │ ├── integration │ │ ├── apigateway │ │ │ ├── apigateway_test.go │ │ │ ├── create.go │ │ │ ├── delete.go │ │ │ ├── find.go │ │ │ ├── main_test.go │ │ │ ├── setup.go │ │ │ ├── teardown.go │ │ │ └── util.go │ │ ├── ec2 │ │ │ ├── create.go │ │ │ ├── delete.go │ │ │ ├── find.go │ │ │ ├── instance_test.go │ │ │ ├── main_test.go │ │ │ ├── setup.go │ │ │ ├── teardown.go │ │ │ └── util.go │ │ ├── errors.go │ │ ├── kms │ │ │ ├── create.go │ │ │ ├── delete.go │ │ │ ├── find.go │ │ │ ├── kms_test.go │ │ │ ├── main_test.go │ │ │ ├── setup.go │ │ │ ├── teardown.go │ │ │ └── util.go │ │ ├── networkmanager │ │ │ ├── create.go │ │ │ ├── delete.go │ │ │ ├── find.go │ │ │ ├── main_test.go │ │ │ ├── networkmanager_test.go │ │ │ ├── setup.go │ │ │ ├── tags.go │ │ │ └── teardown.go │ │ ├── ssm │ │ │ └── main_test.go │ │ ├── util.go │ │ └── util_test.go │ ├── kms-alias.go │ ├── kms-alias_test.go │ ├── kms-custom-key-store.go │ ├── kms-custom-key-store_test.go │ ├── kms-grant.go │ ├── kms-grant_test.go │ ├── kms-key-policy.go │ ├── kms-key-policy_test.go │ ├── kms-key.go │ ├── kms-key_test.go │ ├── kms.go │ ├── lambda-function.go │ ├── lambda-function_test.go │ ├── lambda-layer-version.go │ ├── lambda-layer-version_test.go │ ├── lambda-layer.go │ ├── lambda-layer_test.go │ ├── lambda.go │ ├── lambda_test.go │ ├── main.go │ ├── network-firewall-firewall-policy.go │ ├── network-firewall-firewall-policy_test.go │ ├── network-firewall-firewall.go │ ├── network-firewall-firewall_test.go │ ├── network-firewall-rule-group.go │ ├── network-firewall-rule-group_test.go │ ├── network-firewall-tls-inspection-configuration.go │ ├── network-firewall-tls-inspection-configuration_test.go │ ├── networkfirewall.go │ ├── networkfirewall_test.go │ ├── networkmanager-connect-attachment.go │ ├── networkmanager-connect-attachment_test.go │ ├── networkmanager-connect-peer-association.go │ ├── networkmanager-connect-peer-association_test.go │ ├── networkmanager-connect-peer.go │ ├── networkmanager-connect-peer_test.go │ ├── networkmanager-connection.go │ ├── networkmanager-connection_test.go │ ├── networkmanager-core-network-policy.go │ ├── networkmanager-core-network-policy_test.go │ ├── networkmanager-core-network.go │ ├── networkmanager-core-network_test.go │ ├── networkmanager-device.go │ ├── networkmanager-device_test.go │ ├── networkmanager-global-network.go │ ├── networkmanager-global-network_test.go │ ├── networkmanager-link-association.go │ ├── networkmanager-link-association_test.go │ ├── networkmanager-link.go │ ├── networkmanager-link_test.go │ ├── networkmanager-network-resource-relationship.go │ ├── networkmanager-network-resource-relationship_test.go │ ├── networkmanager-site-to-site-vpn-attachment.go │ ├── networkmanager-site-to-site-vpn-attachment_test.go │ ├── networkmanager-site.go │ ├── networkmanager-site_test.go │ ├── networkmanager-transit-gateway-connect-peer-association.go │ ├── networkmanager-transit-gateway-connect-peer-association_test.go │ ├── networkmanager-transit-gateway-peering.go │ ├── networkmanager-transit-gateway-peering_test.go │ ├── networkmanager-transit-gateway-registration.go │ ├── networkmanager-transit-gateway-registration_test.go │ ├── networkmanager-transit-gateway-route-table-attachment.go │ ├── networkmanager-transit-gateway-route-table-attachment_test.go │ ├── networkmanager-vpc-attachment.go │ ├── networkmanager-vpc-attachment_test.go │ ├── networkmanager.go │ ├── networkmanager_test.go │ ├── rds-db-cluster-parameter-group.go │ ├── rds-db-cluster-parameter-group_test.go │ ├── rds-db-cluster.go │ ├── rds-db-cluster_test.go │ ├── rds-db-instance.go │ ├── rds-db-instance_test.go │ ├── rds-db-parameter-group.go │ ├── rds-db-parameter-group_test.go │ ├── rds-db-subnet-group.go │ ├── rds-db-subnet-group_test.go │ ├── rds-option-group.go │ ├── rds-option-group_test.go │ ├── rds.go │ ├── rds_test.go │ ├── route53-health-check.go │ ├── route53-health-check_test.go │ ├── route53-hosted-zone.go │ ├── route53-hosted-zone_test.go │ ├── route53-resource-record-set.go │ ├── route53-resource-record-set_test.go │ ├── route53.go │ ├── route53_test.go │ ├── s3.go │ ├── s3_test.go │ ├── sns-data-protection-policy.go │ ├── sns-data-protection-policy_test.go │ ├── sns-endpoint.go │ ├── sns-endpoint_test.go │ ├── sns-platform-application.go │ ├── sns-platform-application_test.go │ ├── sns-subscription.go │ ├── sns-subscription_test.go │ ├── sns-topic.go │ ├── sns-topic_test.go │ ├── sns.go │ ├── sqs-queue.go │ ├── sqs-queue_test.go │ ├── sqs.go │ ├── ssm-parameter.go │ ├── ssm-parameter_test.go │ └── tracing.go ├── build │ └── package │ │ └── Dockerfile ├── cmd │ └── root.go ├── docker-compose.yml ├── main.go └── proc │ └── proc.go ├── cmd ├── auth_client.go ├── bookmarks.go ├── bookmarks_create_bookmark.go ├── bookmarks_get_affected_bookmarks.go ├── bookmarks_get_bookmark.go ├── changes.go ├── changes_end_change.go ├── changes_get_change.go ├── changes_list_changes.go ├── changes_start_change.go ├── changes_submit_plan.go ├── changes_submit_plan_test.go ├── explore.go ├── flags.go ├── integrations.go ├── integrations_tfc.go ├── invites.go ├── invites_crud.go ├── logging.go ├── pterm.go ├── repo.go ├── repo_test.go ├── request.go ├── request_load.go ├── request_query.go ├── root.go ├── root_test.go ├── snapshots.go ├── snapshots_get_snapshot.go ├── terraform.go ├── terraform_apply.go ├── terraform_plan.go ├── theme.go ├── theme_darwin.go ├── theme_linux.go ├── theme_test.go └── theme_windows.go ├── demos └── plan.tape ├── discovery ├── adapter.go ├── adapter_test.go ├── adapterhost.go ├── adapterhost_test.go ├── cmd.go ├── cmd_test.go ├── engine.go ├── engine_test.go ├── enginerequests.go ├── enginerequests_test.go ├── getfindmutex.go ├── getfindmutex_test.go ├── heartbeat.go ├── heartbeat_test.go ├── item_tests.go ├── logs.go ├── logs_test.go ├── main_test.go ├── nats_shared_test.go ├── nats_watcher.go ├── nats_watcher_test.go ├── nil_publisher.go ├── performance_test.go ├── querytracker.go ├── querytracker_test.go ├── shared_test.go └── tracing.go ├── examples └── create-bookmark.json ├── go.mod ├── go.sum ├── gon-amd64.json ├── gon-arm64.json ├── k8s-source ├── acceptance │ └── nats-server.conf ├── adapters │ ├── clusterrole.go │ ├── clusterrole_test.go │ ├── clusterrolebinding.go │ ├── clusterrolebinding_test.go │ ├── configmap.go │ ├── configmap_test.go │ ├── cronjob.go │ ├── cronjob_test.go │ ├── daemonset.go │ ├── daemonset_test.go │ ├── deployment.go │ ├── deployment_test.go │ ├── endpoints.go │ ├── endpoints_test.go │ ├── endpointslice.go │ ├── endpointslice_test.go │ ├── generic_source.go │ ├── generic_source_test.go │ ├── horizontalpodautoscaler.go │ ├── horizontalpodautoscaler_test.go │ ├── ingress.go │ ├── ingress_test.go │ ├── job.go │ ├── job_test.go │ ├── limitrange.go │ ├── limitrange_test.go │ ├── main.go │ ├── networkpolicy.go │ ├── networkpolicy_test.go │ ├── node.go │ ├── node_test.go │ ├── persistentvolume.go │ ├── persistentvolume_test.go │ ├── persistentvolumeclaim.go │ ├── persistentvolumeclaim_test.go │ ├── poddisruptionbudget.go │ ├── poddisruptionbudget_test.go │ ├── pods.go │ ├── pods_test.go │ ├── priorityclass.go │ ├── priorityclass_test.go │ ├── replicaset.go │ ├── replicaset_test.go │ ├── replicationcontroller.go │ ├── replicationcontroller_test.go │ ├── resourcequota.go │ ├── resourcequota_test.go │ ├── role.go │ ├── role_test.go │ ├── rolebinding.go │ ├── rolebinding_test.go │ ├── secret.go │ ├── secret_test.go │ ├── service.go │ ├── service_test.go │ ├── serviceaccount.go │ ├── serviceaccount_test.go │ ├── shared_test.go │ ├── shared_util.go │ ├── shared_util_test.go │ ├── statefulset.go │ ├── statefulset_test.go │ ├── storageclass.go │ ├── storageclass_test.go │ ├── volumeattachment.go │ └── volumeattachment_test.go ├── build │ └── package │ │ └── Dockerfile ├── cmd │ └── root.go ├── config.json ├── cr.sh ├── deployments │ └── overmind-kube-source │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── clusterrole.yaml │ │ ├── clusterrolebinding.yaml │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── secret.yaml │ │ └── serviceaccount.yaml │ │ └── values.yaml └── main.go ├── lostpixel.config.ts ├── main.go ├── main.tf ├── package.json ├── sdp-go ├── .gitignore ├── account.go ├── account.pb.go ├── apikey.go ├── apikeys.pb.go ├── auth0support.pb.go ├── bookmarks.go ├── bookmarks.pb.go ├── changes.go ├── changes.pb.go ├── changes_test.go ├── changetimeline.go ├── cli.pb.go ├── compare.go ├── config.pb.go ├── connection.go ├── connection_test.go ├── encoder_test.go ├── errors.go ├── gateway.go ├── gateway.pb.go ├── gateway_test.go ├── genhandler.go ├── graph │ ├── main.go │ └── main_test.go ├── handler_cancelquery.go ├── handler_gatewayresponse.go ├── handler_natsgetlogrecordsrequest.go ├── handler_natsgetlogrecordsresponse.go ├── handler_query.go ├── handler_queryresponse.go ├── instance_detect.go ├── invites.pb.go ├── items.go ├── items.pb.go ├── items_test.go ├── link_extract.go ├── link_extract_test.go ├── logs.go ├── logs.pb.go ├── logs_test.go ├── progress.go ├── progress_test.go ├── responses.go ├── responses.pb.go ├── revlink.pb.go ├── sdpconnect │ ├── account.connect.go │ ├── apikeys.connect.go │ ├── auth0support.connect.go │ ├── bookmarks.connect.go │ ├── changes.connect.go │ ├── cli.connect.go │ ├── config.connect.go │ ├── invites.connect.go │ ├── logs.connect.go │ ├── revlink.connect.go │ └── snapshots.connect.go ├── sdpws │ ├── client.go │ ├── client_test.go │ ├── messagehandler.go │ └── utils.go ├── snapshots.go ├── snapshots.pb.go ├── test_utils.go ├── test_utils_test.go ├── tracing.go ├── tracing │ └── main.go ├── tracing_test.go ├── util.go ├── util.pb.go ├── util_test.go ├── validation.go └── validation_test.go ├── sdpcache ├── cache.go ├── cache_benchmark_test.go ├── cache_test.go └── item_generator_test.go ├── sources ├── aws │ ├── apigateway-api-key.go │ ├── apigateway-stage.go │ ├── base.go │ ├── errors.go │ ├── shared │ │ └── models.go │ └── validation_test.go ├── example │ ├── base.go │ ├── custom_searchable_listable.go │ ├── errors.go │ ├── metadata_test.go │ ├── mocks │ │ └── mock_external_api_client.go │ ├── shared │ │ └── models.go │ ├── standard_searchable_listable.go │ ├── standard_searchable_listable_test.go │ └── validation_test.go ├── gcp │ ├── README.md │ ├── adapters │ │ ├── adapters.go │ │ ├── compute-accelerator-type.go │ │ ├── compute-address.go │ │ ├── compute-address_test.go │ │ ├── compute-autoscaler.go │ │ ├── compute-autoscaler_test.go │ │ ├── compute-backend-service.go │ │ ├── compute-backend-service_test.go │ │ ├── compute-forwarding-rule.go │ │ ├── compute-forwarding-rule_test.go │ │ ├── compute-healthcheck.go │ │ ├── compute-healthcheck_test.go │ │ ├── compute-image.go │ │ ├── compute-image_test.go │ │ ├── compute-instance-group-manager.go │ │ ├── compute-instance-group-manager_test.go │ │ ├── compute-instance-group.go │ │ ├── compute-instance-group_test.go │ │ ├── compute-instance.go │ │ ├── compute-instance_test.go │ │ ├── compute-instant-snapshot.go │ │ ├── compute-instant-snapshot_test.go │ │ ├── compute-machine-type.go │ │ ├── compute-node-group.go │ │ ├── compute-node-group_test.go │ │ ├── compute-node-template.go │ │ ├── compute-node-template_test.go │ │ ├── compute-region-commitment.go │ │ ├── compute-reservation.go │ │ ├── compute-reservation_test.go │ │ ├── compute-resource-policy.go │ │ ├── compute-security-policy.go │ │ ├── compute-security-policy_test.go │ │ ├── container-cluster.go │ │ ├── network-security-client-tls-policy.go │ │ ├── network-services-service-binding.go │ │ └── network-services-service-lb-policy.go │ ├── cmd │ │ └── root.go │ ├── integration-tests │ │ ├── compute-address_test.go │ │ ├── compute-autoscaler_test.go │ │ ├── compute-forwarding-rule_test.go │ │ ├── compute-healthcheck_test.go │ │ ├── compute-image_test.go │ │ ├── compute-instance-group-manager_test.go │ │ ├── compute-instance-group_test.go │ │ ├── compute-instance_test.go │ │ ├── compute-instant-snapshot_test.go │ │ ├── compute-node-group_test.go │ │ ├── compute-reservation_test.go │ │ └── main_test.go │ ├── main.go │ ├── proc │ │ ├── proc.go │ │ └── proc_test.go │ └── shared │ │ ├── base.go │ │ ├── compute-clients.go │ │ ├── errors.go │ │ ├── mocks │ │ └── mock_compute_instance_client.go │ │ ├── models.go │ │ ├── network-security-clients.go │ │ ├── utils.go │ │ └── utils_test.go ├── shared │ ├── base.go │ ├── shared.go │ ├── testing.go │ ├── util.go │ └── util_test.go ├── stdlib │ ├── items.go │ └── shared │ │ └── models.go ├── transformer.go └── transformer_test.go ├── stdlib-source ├── adapters │ ├── certificate.go │ ├── certificate_test.go │ ├── dns.go │ ├── dns_test.go │ ├── http.go │ ├── http_test.go │ ├── ip.go │ ├── ip_cache.go │ ├── ip_cache_test.go │ ├── ip_test.go │ ├── main.go │ ├── main_test.go │ ├── rdap-asn.go │ ├── rdap-asn_test.go │ ├── rdap-domain.go │ ├── rdap-domain_test.go │ ├── rdap-entity.go │ ├── rdap-entity_test.go │ ├── rdap-ip-network.go │ ├── rdap-ip-network_test.go │ ├── rdap-nameserver.go │ ├── rdap-nameserver_test.go │ └── test │ │ ├── data.go │ │ ├── testdog.go │ │ ├── testfood.go │ │ ├── testgroup.go │ │ ├── testhobby.go │ │ ├── testlocation.go │ │ ├── testperson.go │ │ └── testregion.go ├── build │ └── package │ │ └── Dockerfile ├── cmd │ └── root.go └── main.go ├── tfutils ├── aws_config.go ├── aws_config_test.go ├── plan.go ├── plan_mapper.go ├── plan_mapper_test.go └── testdata │ ├── config_from_provider │ ├── ca-bundle.crt │ └── test.tf │ ├── invalid_vars.tfvars │ ├── plan.json │ ├── providers.tf │ ├── state.json │ ├── subfolder │ └── more_providers.tf │ ├── test_vars.tfvars │ └── tfvars.json └── tracing ├── deferlog.go ├── main.go └── main_test.go /.github/e2eapply.tape: -------------------------------------------------------------------------------- 1 | Output e2e/apply.mp4 2 | 3 | Set Width 1920 4 | Set Height 1080 5 | 6 | Set FontSize 12 7 | Set CursorBlink false 8 | 9 | Hide 10 | Type "export PATH=$PWD:$PATH" 11 | Enter 12 | Type "clear" 13 | Enter 14 | Show 15 | 16 | Type@1ms "overmind terraform apply -- tfplan" 17 | Enter 18 | Sleep 70 19 | Screenshot e2e/apply.png 20 | -------------------------------------------------------------------------------- /.github/e2eplan.tape: -------------------------------------------------------------------------------- 1 | Output e2e/plan.mp4 2 | 3 | Set Width 1920 4 | Set Height 1080 5 | 6 | Set FontSize 12 7 | Set CursorBlink false 8 | 9 | Hide 10 | Type "export PATH=$PWD:$PATH" 11 | Enter 12 | Type "clear" 13 | Enter 14 | Show 15 | 16 | Type@1ms "overmind terraform plan -- -out tfplan" 17 | Enter 18 | Sleep 2 19 | Enter 20 | Sleep 80 21 | Screenshot e2e/plan.png 22 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | on: push 3 | jobs: 4 | test: 5 | name: Run Tests 6 | runs-on: depot-ubuntu-22.04-4 7 | 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version: 1.x 18 | check-latest: true 19 | cache: true 20 | 21 | - name: Go Test 22 | run: | 23 | go run main.go --version 24 | go test -race -v -timeout 5m github.com/overmindtech/cli github.com/overmindtech/cli/tfutils 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | gon 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | 21 | # Go workspace file 22 | go.work 23 | 24 | dist/ 25 | output 26 | .DS_Store 27 | .terraform 28 | overmind.plan 29 | terraform.tfstate 30 | terraform.tfstate.backup 31 | /tmp/ 32 | /node_modules 33 | __debug_bin* 34 | 35 | # ignore local terraform files 36 | tfplan.json* 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Releasing a new cli version 2 | 3 | - Make sure that main is green before proceeding! Then in the CLI directory, update to latest. 4 | 5 | ```shell 6 | git fetch --all 7 | git rebase origin/main 8 | ``` 9 | 10 | - Compare the changes from the last release to what is in main. For [example](https://github.com/overmindtech/cli/compare/v1.3.2...main). Following [semver](https://semver.org/) choose your new version. And use it to tag a version, and push it. 11 | 12 | ```shell 13 | git tag -s v0.0.0 14 | git push origin tag v0.0.0 15 | ``` 16 | 17 | - Github actions will then run, assuming everything goes green. It will create a new [pull request in overmind/homebrew-overmind](https://github.com/overmindtech/homebrew-overmind/pulls). 18 | - **DO NOT MERGE THE PR YET** If this PR is green the PR it will require the 'pr-pull' label to be added. It will trigger another github action / check to run. This will automerge the PR and the release is complete. 19 | -------------------------------------------------------------------------------- /assets/blastRadiusItems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/blastRadiusItems.png -------------------------------------------------------------------------------- /assets/chainLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/chainLink.png -------------------------------------------------------------------------------- /assets/change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/change.png -------------------------------------------------------------------------------- /assets/change.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/changed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/changed.png -------------------------------------------------------------------------------- /assets/changed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/created.png -------------------------------------------------------------------------------- /assets/created.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/deleted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/deleted.png -------------------------------------------------------------------------------- /assets/deleted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/edge.png -------------------------------------------------------------------------------- /assets/high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/high.png -------------------------------------------------------------------------------- /assets/high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/item.png -------------------------------------------------------------------------------- /assets/item.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/logo.png -------------------------------------------------------------------------------- /assets/low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/low.png -------------------------------------------------------------------------------- /assets/low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/medium.png -------------------------------------------------------------------------------- /assets/medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/replaced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/replaced.png -------------------------------------------------------------------------------- /assets/replaced.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/risks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overmindtech/cli/55bfbc5cc407d95db54c909903f0223fb51171ec/assets/risks.png -------------------------------------------------------------------------------- /assets/risks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/unchanged.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/unmapped.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /auth/tracing.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "go.opentelemetry.io/otel" 5 | semconv "go.opentelemetry.io/otel/semconv/v1.24.0" 6 | "go.opentelemetry.io/otel/trace" 7 | ) 8 | 9 | const ( 10 | instrumentationName = "github.com/overmindtech/cli/auth" 11 | instrumentationVersion = "0.0.1" 12 | ) 13 | 14 | var tracer = otel.GetTracerProvider().Tracer( 15 | instrumentationName, 16 | trace.WithInstrumentationVersion(instrumentationVersion), 17 | trace.WithSchemaURL(semconv.SchemaURL), 18 | ) 19 | -------------------------------------------------------------------------------- /aws-source/.deadcode-ignore: -------------------------------------------------------------------------------- 1 | adapterhelpers/shared_tests.go:19:6: unreachable func: PtrInt32 2 | adapterhelpers/shared_tests.go:27:6: unreachable func: PtrFloat32 3 | adapterhelpers/shared_tests.go:31:6: unreachable func: PtrFloat64 4 | adapterhelpers/shared_tests.go:35:6: unreachable func: PtrTime 5 | adapterhelpers/shared_tests.go:39:6: unreachable func: PtrBool 6 | adapterhelpers/shared_tests.go:73:21: unreachable func: VPCConfig.Cleanup 7 | adapterhelpers/shared_tests.go:77:21: unreachable func: VPCConfig.RunCleanup 8 | adapterhelpers/shared_tests.go:88:21: unreachable func: VPCConfig.Fetch 9 | adapterhelpers/shared_tests.go:121:21: unreachable func: VPCConfig.CreateGateway 10 | adapterhelpers/shared_tests.go:198:6: unreachable func: retry 11 | adapterhelpers/shared_tests.go:221:21: unreachable func: QueryTests.Execute 12 | adapterhelpers/shared_tests.go:238:6: unreachable func: lirMatches 13 | adapterhelpers/shared_tests.go:246:6: unreachable func: CheckQuery 14 | adapterhelpers/util.go:180:18: unreachable func: E2ETest.Run 15 | adapterhelpers/util.go:326:6: unreachable func: GetAutoConfig 16 | adapters/rds.go:25:24: unreachable func: mockRdsClient.DescribeDBClusterParameterGroups 17 | adapters/rds.go:29:24: unreachable func: mockRdsClient.DescribeDBClusterParameters 18 | adapters/rds.go:33:24: unreachable func: mockRdsClient.ListTagsForResource 19 | adapters/rds.go:44:24: unreachable func: mockRdsClient.DescribeDBClusters 20 | adapters/rds.go:48:24: unreachable func: mockRdsClient.DescribeDBInstances 21 | adapters/rds.go:52:24: unreachable func: mockRdsClient.DescribeDBSubnetGroups 22 | adapters/rds.go:56:24: unreachable func: mockRdsClient.DescribeOptionGroups 23 | adapters/rds.go:60:24: unreachable func: mockRdsClient.DescribeDBParameterGroups 24 | adapters/rds.go:64:24: unreachable func: mockRdsClient.DescribeDBParameters 25 | -------------------------------------------------------------------------------- /aws-source/acceptance/nats-server.conf: -------------------------------------------------------------------------------- 1 | # Client port of 4222 on all interfaces 2 | port: 4222 3 | 4 | # HTTP monitoring port 5 | monitor_port: 8222 6 | 7 | # This is for clustering multiple servers together. 8 | cluster { 9 | # It is recommended to set a cluster name 10 | name: "my_cluster" 11 | 12 | # Route connections to be received on any interface on port 6222 13 | port: 6222 14 | 15 | # Routes are protected, so need to use them with --routes flag 16 | # e.g. --routes=nats-route://ruser:T0pS3cr3t@otherdockerhost:6222 17 | authorization { 18 | user: ruser 19 | timeout: 0.75 20 | } 21 | 22 | # Routes are actively solicited and connected to from this server. 23 | # This Docker image has none by default, but you can pass a 24 | # flag to the nats-server docker image to create one to an existing server. 25 | routes = [] 26 | } 27 | 28 | websocket { 29 | port: 4433 30 | no_tls: true 31 | } 32 | -------------------------------------------------------------------------------- /aws-source/adapters/apigateway-api-key_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/aws" 8 | "github.com/aws/aws-sdk-go-v2/service/apigateway" 9 | "github.com/aws/aws-sdk-go-v2/service/apigateway/types" 10 | 11 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 12 | "github.com/overmindtech/cli/sdp-go" 13 | ) 14 | 15 | func TestApiKeyOutputMapper(t *testing.T) { 16 | awsItem := &types.ApiKey{ 17 | Id: aws.String("api-key-id"), 18 | Name: aws.String("api-key-name"), 19 | Enabled: true, 20 | CreatedDate: aws.Time(time.Now()), 21 | LastUpdatedDate: aws.Time(time.Now()), 22 | StageKeys: []string{"rest-api-id/stage"}, 23 | Tags: map[string]string{"key": "value"}, 24 | } 25 | 26 | item, err := apiKeyOutputMapper("scope", awsItem) 27 | if err != nil { 28 | t.Fatalf("unexpected error: %v", err) 29 | } 30 | 31 | if err := item.Validate(); err != nil { 32 | t.Error(err) 33 | } 34 | 35 | tests := adapterhelpers.QueryTests{ 36 | { 37 | ExpectedType: "apigateway-rest-api", 38 | ExpectedMethod: sdp.QueryMethod_GET, 39 | ExpectedQuery: "rest-api-id", 40 | ExpectedScope: "scope", 41 | }, 42 | } 43 | 44 | tests.Execute(t, item) 45 | } 46 | 47 | func TestNewAPIGatewayApiKeyAdapter(t *testing.T) { 48 | config, account, region := adapterhelpers.GetAutoConfig(t) 49 | 50 | client := apigateway.NewFromConfig(config) 51 | 52 | adapter := NewAPIGatewayApiKeyAdapter(client, account, region) 53 | 54 | test := adapterhelpers.E2ETest{ 55 | Adapter: adapter, 56 | Timeout: 10 * time.Second, 57 | SkipList: true, 58 | } 59 | 60 | test.Run(t) 61 | } 62 | -------------------------------------------------------------------------------- /aws-source/adapters/apigateway-deployment_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/aws" 8 | "github.com/aws/aws-sdk-go-v2/service/apigateway" 9 | "github.com/aws/aws-sdk-go-v2/service/apigateway/types" 10 | 11 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 12 | "github.com/overmindtech/cli/sdp-go" 13 | ) 14 | 15 | func TestDeploymentOutputMapper(t *testing.T) { 16 | awsItem := &types.Deployment{ 17 | Id: aws.String("deployment-id"), 18 | CreatedDate: aws.Time(time.Now()), 19 | Description: aws.String("deployment-description"), 20 | ApiSummary: map[string]map[string]types.MethodSnapshot{}, 21 | } 22 | 23 | item, err := deploymentOutputMapper("rest-api-id", "scope", awsItem) 24 | if err != nil { 25 | t.Fatalf("unexpected error: %v", err) 26 | } 27 | 28 | if err := item.Validate(); err != nil { 29 | t.Error(err) 30 | } 31 | 32 | tests := adapterhelpers.QueryTests{ 33 | { 34 | ExpectedType: "apigateway-rest-api", 35 | ExpectedMethod: sdp.QueryMethod_GET, 36 | ExpectedQuery: "rest-api-id", 37 | ExpectedScope: "scope", 38 | }, 39 | { 40 | ExpectedType: "apigateway-stage", 41 | ExpectedMethod: sdp.QueryMethod_SEARCH, 42 | ExpectedQuery: "rest-api-id/deployment-id", 43 | ExpectedScope: "scope", 44 | }, 45 | } 46 | 47 | tests.Execute(t, item) 48 | } 49 | 50 | func TestNewAPIGatewayDeploymentAdapter(t *testing.T) { 51 | config, account, region := adapterhelpers.GetAutoConfig(t) 52 | 53 | client := apigateway.NewFromConfig(config) 54 | 55 | adapter := NewAPIGatewayDeploymentAdapter(client, account, region) 56 | 57 | test := adapterhelpers.E2ETest{ 58 | Adapter: adapter, 59 | Timeout: 10 * time.Second, 60 | SkipList: true, 61 | } 62 | 63 | test.Run(t) 64 | } 65 | -------------------------------------------------------------------------------- /aws-source/adapters/apigateway-model_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/aws" 8 | "github.com/aws/aws-sdk-go-v2/service/apigateway" 9 | "github.com/aws/aws-sdk-go-v2/service/apigateway/types" 10 | 11 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 12 | "github.com/overmindtech/cli/sdp-go" 13 | ) 14 | 15 | func TestModelOutputMapper(t *testing.T) { 16 | awsItem := &types.Model{ 17 | Id: aws.String("model-id"), 18 | Name: aws.String("model-name"), 19 | Description: aws.String("description"), 20 | Schema: aws.String("{\"type\": \"object\"}"), 21 | ContentType: aws.String("application/json"), 22 | } 23 | 24 | item, err := modelOutputMapper("rest-api-id/model-name", "scope", awsItem) 25 | if err != nil { 26 | t.Fatalf("unexpected error: %v", err) 27 | } 28 | 29 | if err := item.Validate(); err != nil { 30 | t.Error(err) 31 | } 32 | 33 | tests := adapterhelpers.QueryTests{ 34 | { 35 | ExpectedType: "apigateway-rest-api", 36 | ExpectedMethod: sdp.QueryMethod_GET, 37 | ExpectedQuery: "rest-api-id", 38 | ExpectedScope: "scope", 39 | }, 40 | } 41 | 42 | tests.Execute(t, item) 43 | } 44 | 45 | func TestNewAPIGatewayModelAdapter(t *testing.T) { 46 | config, account, region := adapterhelpers.GetAutoConfig(t) 47 | 48 | client := apigateway.NewFromConfig(config) 49 | 50 | adapter := NewAPIGatewayModelAdapter(client, account, region) 51 | 52 | test := adapterhelpers.E2ETest{ 53 | Adapter: adapter, 54 | Timeout: 10 * time.Second, 55 | SkipList: true, 56 | } 57 | 58 | test.Run(t) 59 | } 60 | -------------------------------------------------------------------------------- /aws-source/adapters/cloudfront-function_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | ) 10 | 11 | func TestFunctionItemMapper(t *testing.T) { 12 | summary := types.FunctionSummary{ 13 | FunctionConfig: &types.FunctionConfig{ 14 | Comment: adapterhelpers.PtrString("test-comment"), 15 | Runtime: types.FunctionRuntimeCloudfrontJs20, 16 | }, 17 | FunctionMetadata: &types.FunctionMetadata{ 18 | FunctionARN: adapterhelpers.PtrString("arn:aws:cloudfront::123456789012:function/test-function"), 19 | LastModifiedTime: adapterhelpers.PtrTime(time.Now()), 20 | CreatedTime: adapterhelpers.PtrTime(time.Now()), 21 | Stage: types.FunctionStageLive, 22 | }, 23 | Name: adapterhelpers.PtrString("test-function"), 24 | Status: adapterhelpers.PtrString("test-status"), 25 | } 26 | 27 | item, err := functionItemMapper("", "test", &summary) 28 | 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | if err = item.Validate(); err != nil { 34 | t.Error(err) 35 | } 36 | } 37 | 38 | func TestNewCloudfrontCloudfrontFunctionAdapter(t *testing.T) { 39 | client, account, _ := CloudfrontGetAutoConfig(t) 40 | 41 | adapter := NewCloudfrontCloudfrontFunctionAdapter(client, account) 42 | 43 | test := adapterhelpers.E2ETest{ 44 | Adapter: adapter, 45 | Timeout: 10 * time.Second, 46 | } 47 | 48 | test.Run(t) 49 | } 50 | -------------------------------------------------------------------------------- /aws-source/adapters/cloudfront-key-group_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | ) 10 | 11 | func TestKeyGroupItemMapper(t *testing.T) { 12 | group := types.KeyGroup{ 13 | Id: adapterhelpers.PtrString("test-id"), 14 | KeyGroupConfig: &types.KeyGroupConfig{ 15 | Items: []string{ 16 | "some-identity", 17 | }, 18 | Name: adapterhelpers.PtrString("test-name"), 19 | Comment: adapterhelpers.PtrString("test-comment"), 20 | }, 21 | LastModifiedTime: adapterhelpers.PtrTime(time.Now()), 22 | } 23 | 24 | item, err := KeyGroupItemMapper("", "test", &group) 25 | 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | if err = item.Validate(); err != nil { 31 | t.Error(err) 32 | } 33 | } 34 | 35 | func TestNewCloudfrontKeyGroupAdapter(t *testing.T) { 36 | client, account, _ := CloudfrontGetAutoConfig(t) 37 | 38 | adapter := NewCloudfrontKeyGroupAdapter(client, account) 39 | 40 | test := adapterhelpers.E2ETest{ 41 | Adapter: adapter, 42 | Timeout: 10 * time.Second, 43 | } 44 | 45 | test.Run(t) 46 | } 47 | -------------------------------------------------------------------------------- /aws-source/adapters/cloudfront-origin-access-control_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | ) 10 | 11 | func TestOriginAccessControlItemMapper(t *testing.T) { 12 | x := types.OriginAccessControl{ 13 | Id: adapterhelpers.PtrString("test"), 14 | OriginAccessControlConfig: &types.OriginAccessControlConfig{ 15 | Name: adapterhelpers.PtrString("example-name"), 16 | OriginAccessControlOriginType: types.OriginAccessControlOriginTypesS3, 17 | SigningBehavior: types.OriginAccessControlSigningBehaviorsAlways, 18 | SigningProtocol: types.OriginAccessControlSigningProtocolsSigv4, 19 | Description: adapterhelpers.PtrString("example-description"), 20 | }, 21 | } 22 | 23 | item, err := originAccessControlItemMapper("", "test", &x) 24 | 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | if err = item.Validate(); err != nil { 30 | t.Error(err) 31 | } 32 | } 33 | 34 | func TestNewCloudfrontOriginAccessControlAdapter(t *testing.T) { 35 | client, account, _ := CloudfrontGetAutoConfig(t) 36 | 37 | adapter := NewCloudfrontOriginAccessControlAdapter(client, account) 38 | 39 | test := adapterhelpers.E2ETest{ 40 | Adapter: adapter, 41 | Timeout: 10 * time.Second, 42 | } 43 | 44 | test.Run(t) 45 | } 46 | -------------------------------------------------------------------------------- /aws-source/adapters/cloudfront.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/cloudfront" 7 | "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" 8 | ) 9 | 10 | // Converts a CloudFront Tags object to a map 11 | func cloudfrontTagsToMap(tags *types.Tags) map[string]string { 12 | if tags == nil { 13 | return nil 14 | } 15 | 16 | tagMap := make(map[string]string) 17 | 18 | for _, tag := range tags.Items { 19 | if tag.Key != nil && tag.Value != nil { 20 | tagMap[*tag.Key] = *tag.Value 21 | } 22 | } 23 | 24 | return tagMap 25 | } 26 | 27 | type CloudFrontClient interface { 28 | GetCachePolicy(ctx context.Context, params *cloudfront.GetCachePolicyInput, optFns ...func(*cloudfront.Options)) (*cloudfront.GetCachePolicyOutput, error) 29 | ListCachePolicies(ctx context.Context, params *cloudfront.ListCachePoliciesInput, optFns ...func(*cloudfront.Options)) (*cloudfront.ListCachePoliciesOutput, error) 30 | 31 | GetDistribution(ctx context.Context, params *cloudfront.GetDistributionInput, optFns ...func(*cloudfront.Options)) (*cloudfront.GetDistributionOutput, error) 32 | ListDistributions(ctx context.Context, params *cloudfront.ListDistributionsInput, optFns ...func(*cloudfront.Options)) (*cloudfront.ListDistributionsOutput, error) 33 | 34 | GetStreamingDistribution(ctx context.Context, params *cloudfront.GetStreamingDistributionInput, optFns ...func(*cloudfront.Options)) (*cloudfront.GetStreamingDistributionOutput, error) 35 | ListStreamingDistributions(ctx context.Context, params *cloudfront.ListStreamingDistributionsInput, optFns ...func(*cloudfront.Options)) (*cloudfront.ListStreamingDistributionsOutput, error) 36 | 37 | ListTagsForResource(ctx context.Context, params *cloudfront.ListTagsForResourceInput, optFns ...func(*cloudfront.Options)) (*cloudfront.ListTagsForResourceOutput, error) 38 | } 39 | -------------------------------------------------------------------------------- /aws-source/adapters/cloudfront_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/cloudfront" 8 | "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" 9 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 10 | ) 11 | 12 | func (c TestCloudFrontClient) ListTagsForResource(ctx context.Context, params *cloudfront.ListTagsForResourceInput, optFns ...func(*cloudfront.Options)) (*cloudfront.ListTagsForResourceOutput, error) { 13 | return &cloudfront.ListTagsForResourceOutput{ 14 | Tags: &types.Tags{ 15 | Items: []types.Tag{ 16 | { 17 | Key: adapterhelpers.PtrString("foo"), 18 | Value: adapterhelpers.PtrString("bar"), 19 | }, 20 | }, 21 | }, 22 | }, nil 23 | } 24 | 25 | type TestCloudFrontClient struct{} 26 | 27 | func CloudfrontGetAutoConfig(t *testing.T) (*cloudfront.Client, string, string) { 28 | config, account, region := adapterhelpers.GetAutoConfig(t) 29 | client := cloudfront.NewFromConfig(config) 30 | 31 | return client, account, region 32 | } 33 | -------------------------------------------------------------------------------- /aws-source/adapters/directconnect-customer-metadata_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/directconnect" 9 | "github.com/aws/aws-sdk-go-v2/service/directconnect/types" 10 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 11 | ) 12 | 13 | func TestCustomerMetadataOutputMapper(t *testing.T) { 14 | output := &directconnect.DescribeCustomerMetadataOutput{ 15 | Agreements: []types.CustomerAgreement{ 16 | { 17 | AgreementName: adapterhelpers.PtrString("example-customer-agreement"), 18 | Status: adapterhelpers.PtrString("signed"), 19 | }, 20 | }, 21 | } 22 | 23 | items, err := customerMetadataOutputMapper(context.Background(), nil, "foo", nil, output) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | for _, item := range items { 29 | if err := item.Validate(); err != nil { 30 | t.Error(err) 31 | } 32 | } 33 | 34 | if len(items) != 1 { 35 | t.Fatalf("expected 1 item, got %v", len(items)) 36 | } 37 | } 38 | 39 | func TestNewDirectConnectCustomerMetadataAdapter(t *testing.T) { 40 | client, account, region := directconnectGetAutoConfig(t) 41 | 42 | adapter := NewDirectConnectCustomerMetadataAdapter(client, account, region) 43 | 44 | test := adapterhelpers.E2ETest{ 45 | Adapter: adapter, 46 | Timeout: 10 * time.Second, 47 | } 48 | 49 | test.Run(t) 50 | } 51 | -------------------------------------------------------------------------------- /aws-source/adapters/directconnect-location_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/directconnect" 9 | "github.com/aws/aws-sdk-go-v2/service/directconnect/types" 10 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 11 | ) 12 | 13 | func TestLocationOutputMapper(t *testing.T) { 14 | output := &directconnect.DescribeLocationsOutput{ 15 | Locations: []types.Location{ 16 | { 17 | AvailableMacSecPortSpeeds: []string{"1 Gbps", "10 Gbps"}, 18 | AvailablePortSpeeds: []string{"50 Mbps", "100 Mbps", "1 Gbps", "10 Gbps"}, 19 | AvailableProviders: []string{"ProviderA", "ProviderB", "ProviderC"}, 20 | LocationName: adapterhelpers.PtrString("NAP do Brasil, Barueri, Sao Paulo"), 21 | LocationCode: adapterhelpers.PtrString("TNDB"), 22 | Region: adapterhelpers.PtrString("us-east-1"), 23 | }, 24 | }, 25 | } 26 | 27 | items, err := locationOutputMapper(context.Background(), nil, "foo", nil, output) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | for _, item := range items { 33 | if err := item.Validate(); err != nil { 34 | t.Error(err) 35 | } 36 | } 37 | 38 | if len(items) != 1 { 39 | t.Fatalf("expected 1 item, got %v", len(items)) 40 | } 41 | } 42 | 43 | func TestNewDirectConnectLocationAdapter(t *testing.T) { 44 | client, account, region := directconnectGetAutoConfig(t) 45 | 46 | adapter := NewDirectConnectLocationAdapter(client, account, region) 47 | 48 | test := adapterhelpers.E2ETest{ 49 | Adapter: adapter, 50 | Timeout: 10 * time.Second, 51 | } 52 | 53 | test.Run(t) 54 | } 55 | -------------------------------------------------------------------------------- /aws-source/adapters/directconnect-virtual-gateway_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/directconnect" 9 | "github.com/aws/aws-sdk-go-v2/service/directconnect/types" 10 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 11 | ) 12 | 13 | func TestVirtualGatewayOutputMapper(t *testing.T) { 14 | output := &directconnect.DescribeVirtualGatewaysOutput{ 15 | VirtualGateways: []types.VirtualGateway{ 16 | { 17 | VirtualGatewayId: adapterhelpers.PtrString("cf68415c-f4ae-48f2-87a7-3b52cexample"), 18 | VirtualGatewayState: adapterhelpers.PtrString("available"), 19 | }, 20 | }, 21 | } 22 | 23 | items, err := virtualGatewayOutputMapper(context.Background(), nil, "foo", nil, output) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | for _, item := range items { 29 | if err := item.Validate(); err != nil { 30 | t.Error(err) 31 | } 32 | } 33 | 34 | if len(items) != 1 { 35 | t.Fatalf("expected 1 item, got %v", len(items)) 36 | } 37 | } 38 | 39 | func TestNewDirectConnectVirtualGatewayAdapter(t *testing.T) { 40 | client, account, region := directconnectGetAutoConfig(t) 41 | 42 | adapter := NewDirectConnectVirtualGatewayAdapter(client, account, region) 43 | 44 | test := adapterhelpers.E2ETest{ 45 | Adapter: adapter, 46 | Timeout: 10 * time.Second, 47 | } 48 | 49 | test.Run(t) 50 | } 51 | -------------------------------------------------------------------------------- /aws-source/adapters/directconnect.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/directconnect" 7 | "github.com/aws/aws-sdk-go-v2/service/directconnect/types" 8 | ) 9 | 10 | // Converts a slice of tags to a map 11 | func directconnectTagsToMap(tags []types.Tag) map[string]string { 12 | tagsMap := make(map[string]string) 13 | 14 | for _, tag := range tags { 15 | if tag.Key != nil && tag.Value != nil { 16 | tagsMap[*tag.Key] = *tag.Value 17 | } 18 | } 19 | 20 | return tagsMap 21 | } 22 | 23 | func arnToTags(ctx context.Context, cli *directconnect.Client, resourceARNs []string) (map[string][]types.Tag, error) { 24 | if cli == nil { 25 | return nil, nil 26 | } 27 | 28 | tagsOutput, err := cli.DescribeTags(ctx, &directconnect.DescribeTagsInput{ 29 | ResourceArns: resourceARNs, 30 | }) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | tags := make(map[string][]types.Tag, len(tagsOutput.ResourceTags)) 36 | for _, tag := range tagsOutput.ResourceTags { 37 | tags[*tag.ResourceArn] = tag.Tags 38 | } 39 | 40 | return tags, nil 41 | } 42 | -------------------------------------------------------------------------------- /aws-source/adapters/directconnect_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/directconnect" 7 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 8 | ) 9 | 10 | func directconnectGetAutoConfig(t *testing.T) (*directconnect.Client, string, string) { 11 | config, account, region := adapterhelpers.GetAutoConfig(t) 12 | client := directconnect.NewFromConfig(config) 13 | 14 | return client, account, region 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/dynamodb.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/dynamodb" 7 | ) 8 | 9 | type Client interface { 10 | DescribeKinesisStreamingDestination(ctx context.Context, params *dynamodb.DescribeKinesisStreamingDestinationInput, optFns ...func(*dynamodb.Options)) (*dynamodb.DescribeKinesisStreamingDestinationOutput, error) 11 | DescribeBackup(ctx context.Context, params *dynamodb.DescribeBackupInput, optFns ...func(*dynamodb.Options)) (*dynamodb.DescribeBackupOutput, error) 12 | ListBackups(ctx context.Context, params *dynamodb.ListBackupsInput, optFns ...func(*dynamodb.Options)) (*dynamodb.ListBackupsOutput, error) 13 | ListTagsOfResource(ctx context.Context, params *dynamodb.ListTagsOfResourceInput, optFns ...func(*dynamodb.Options)) (*dynamodb.ListTagsOfResourceOutput, error) 14 | 15 | dynamodb.DescribeTableAPIClient 16 | dynamodb.ListTablesAPIClient 17 | } 18 | -------------------------------------------------------------------------------- /aws-source/adapters/dynamodb_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | type DynamoDBTestClient struct{} 4 | -------------------------------------------------------------------------------- /aws-source/adapters/ec2.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import "github.com/aws/aws-sdk-go-v2/service/ec2/types" 4 | 5 | // Converts a slice of tags to a map 6 | func ec2TagsToMap(tags []types.Tag) map[string]string { 7 | tagsMap := make(map[string]string) 8 | 9 | for _, tag := range tags { 10 | if tag.Key != nil && tag.Value != nil { 11 | tagsMap[*tag.Key] = *tag.Value 12 | } 13 | } 14 | 15 | return tagsMap 16 | } 17 | -------------------------------------------------------------------------------- /aws-source/adapters/ec2_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/ec2" 7 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 8 | ) 9 | 10 | func ec2GetAutoConfig(t *testing.T) (*ec2.Client, string, string) { 11 | config, account, region := adapterhelpers.GetAutoConfig(t) 12 | client := ec2.NewFromConfig(config) 13 | 14 | return client, account, region 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/ecs.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/ecs" 7 | "github.com/aws/aws-sdk-go-v2/service/ecs/types" 8 | ) 9 | 10 | type ECSClient interface { 11 | DescribeClusters(ctx context.Context, params *ecs.DescribeClustersInput, optFns ...func(*ecs.Options)) (*ecs.DescribeClustersOutput, error) 12 | DescribeCapacityProviders(ctx context.Context, params *ecs.DescribeCapacityProvidersInput, optFns ...func(*ecs.Options)) (*ecs.DescribeCapacityProvidersOutput, error) 13 | DescribeContainerInstances(ctx context.Context, params *ecs.DescribeContainerInstancesInput, optFns ...func(*ecs.Options)) (*ecs.DescribeContainerInstancesOutput, error) 14 | DescribeServices(ctx context.Context, params *ecs.DescribeServicesInput, optFns ...func(*ecs.Options)) (*ecs.DescribeServicesOutput, error) 15 | DescribeTaskDefinition(ctx context.Context, params *ecs.DescribeTaskDefinitionInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTaskDefinitionOutput, error) 16 | DescribeTasks(ctx context.Context, params *ecs.DescribeTasksInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error) 17 | 18 | ecs.ListClustersAPIClient 19 | ecs.ListContainerInstancesAPIClient 20 | ecs.ListServicesAPIClient 21 | ecs.ListTaskDefinitionsAPIClient 22 | ecs.ListTasksAPIClient 23 | } 24 | 25 | // convertTags converts slice of ecs tags to a map 26 | func ecsTagsToMap(tags []types.Tag) map[string]string { 27 | tagsMap := make(map[string]string) 28 | 29 | for _, tag := range tags { 30 | if tag.Key != nil && tag.Value != nil { 31 | tagsMap[*tag.Key] = *tag.Value 32 | } 33 | } 34 | 35 | return tagsMap 36 | } 37 | -------------------------------------------------------------------------------- /aws-source/adapters/ecs_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/ecs" 7 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 8 | ) 9 | 10 | type ecsTestClient struct{} 11 | 12 | func ecsGetAutoConfig(t *testing.T) (*ecs.Client, string, string) { 13 | config, account, region := adapterhelpers.GetAutoConfig(t) 14 | client := ecs.NewFromConfig(config) 15 | 16 | return client, account, region 17 | } 18 | -------------------------------------------------------------------------------- /aws-source/adapters/efs-backup-policy_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/efs" 8 | "github.com/aws/aws-sdk-go-v2/service/efs/types" 9 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 10 | ) 11 | 12 | func TestBackupPolicyOutputMapper(t *testing.T) { 13 | output := &efs.DescribeBackupPolicyOutput{ 14 | BackupPolicy: &types.BackupPolicy{ 15 | Status: types.StatusEnabled, 16 | }, 17 | } 18 | 19 | items, err := BackupPolicyOutputMapper(context.Background(), nil, "foo", &efs.DescribeBackupPolicyInput{ 20 | FileSystemId: adapterhelpers.PtrString("fs-1234"), 21 | }, output) 22 | 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | for _, item := range items { 28 | if err := item.Validate(); err != nil { 29 | t.Error(err) 30 | } 31 | } 32 | 33 | if len(items) != 1 { 34 | t.Fatalf("expected 1 item, got %v", len(items)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /aws-source/adapters/efs.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go-v2/service/efs/types" 5 | "github.com/overmindtech/cli/sdp-go" 6 | ) 7 | 8 | // lifeCycleStateToHealth Converts a lifecycle state to a health state 9 | func lifeCycleStateToHealth(state types.LifeCycleState) *sdp.Health { 10 | switch state { 11 | case types.LifeCycleStateCreating: 12 | return sdp.Health_HEALTH_PENDING.Enum() 13 | case types.LifeCycleStateAvailable: 14 | return sdp.Health_HEALTH_OK.Enum() 15 | case types.LifeCycleStateUpdating: 16 | return sdp.Health_HEALTH_PENDING.Enum() 17 | case types.LifeCycleStateDeleting: 18 | return sdp.Health_HEALTH_PENDING.Enum() 19 | case types.LifeCycleStateDeleted: 20 | return sdp.Health_HEALTH_WARNING.Enum() 21 | case types.LifeCycleStateError: 22 | return sdp.Health_HEALTH_ERROR.Enum() 23 | } 24 | 25 | return nil 26 | } 27 | 28 | // Converts a slice of tags to a map 29 | func efsTagsToMap(tags []types.Tag) map[string]string { 30 | tagsMap := make(map[string]string) 31 | 32 | for _, tag := range tags { 33 | if tag.Key != nil && tag.Value != nil { 34 | tagsMap[*tag.Key] = *tag.Value 35 | } 36 | } 37 | 38 | return tagsMap 39 | } 40 | -------------------------------------------------------------------------------- /aws-source/adapters/efs_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/efs" 7 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 8 | ) 9 | 10 | func efsGetAutoConfig(t *testing.T) (*efs.Client, string, string) { 11 | config, account, region := adapterhelpers.GetAutoConfig(t) 12 | client := efs.NewFromConfig(config) 13 | 14 | return client, account, region 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/eks.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/eks" 7 | ) 8 | 9 | type EKSClient interface { 10 | ListClusters(context.Context, *eks.ListClustersInput, ...func(*eks.Options)) (*eks.ListClustersOutput, error) 11 | DescribeCluster(ctx context.Context, params *eks.DescribeClusterInput, optFns ...func(*eks.Options)) (*eks.DescribeClusterOutput, error) 12 | ListAddons(context.Context, *eks.ListAddonsInput, ...func(*eks.Options)) (*eks.ListAddonsOutput, error) 13 | DescribeAddon(ctx context.Context, params *eks.DescribeAddonInput, optFns ...func(*eks.Options)) (*eks.DescribeAddonOutput, error) 14 | ListFargateProfiles(ctx context.Context, params *eks.ListFargateProfilesInput, optFns ...func(*eks.Options)) (*eks.ListFargateProfilesOutput, error) 15 | DescribeFargateProfile(ctx context.Context, params *eks.DescribeFargateProfileInput, optFns ...func(*eks.Options)) (*eks.DescribeFargateProfileOutput, error) 16 | ListIdentityProviderConfigs(ctx context.Context, params *eks.ListIdentityProviderConfigsInput, optFns ...func(*eks.Options)) (*eks.ListIdentityProviderConfigsOutput, error) 17 | DescribeIdentityProviderConfig(ctx context.Context, params *eks.DescribeIdentityProviderConfigInput, optFns ...func(*eks.Options)) (*eks.DescribeIdentityProviderConfigOutput, error) 18 | ListNodegroups(ctx context.Context, params *eks.ListNodegroupsInput, optFns ...func(*eks.Options)) (*eks.ListNodegroupsOutput, error) 19 | DescribeNodegroup(ctx context.Context, params *eks.DescribeNodegroupInput, optFns ...func(*eks.Options)) (*eks.DescribeNodegroupOutput, error) 20 | } 21 | -------------------------------------------------------------------------------- /aws-source/adapters/elb-instance-health_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" 8 | "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" 9 | 10 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 11 | "github.com/overmindtech/cli/sdp-go" 12 | ) 13 | 14 | func TestInstanceHealthOutputMapper(t *testing.T) { 15 | 16 | output := elb.DescribeInstanceHealthOutput{ 17 | InstanceStates: []types.InstanceState{ 18 | { 19 | InstanceId: adapterhelpers.PtrString("i-0337802d908b4a81e"), // link 20 | State: adapterhelpers.PtrString("InService"), 21 | ReasonCode: adapterhelpers.PtrString("N/A"), 22 | Description: adapterhelpers.PtrString("N/A"), 23 | }, 24 | }, 25 | } 26 | 27 | items, err := instanceHealthOutputMapper(context.Background(), nil, "foo", nil, &output) 28 | 29 | if err != nil { 30 | t.Error(err) 31 | } 32 | 33 | for _, item := range items { 34 | if err := item.Validate(); err != nil { 35 | t.Error(err) 36 | } 37 | } 38 | 39 | if len(items) != 1 { 40 | t.Fatalf("expected 1 item, got %v", len(items)) 41 | } 42 | 43 | item := items[0] 44 | 45 | // It doesn't really make sense to test anything other than the linked items 46 | // since the attributes are converted automatically 47 | tests := adapterhelpers.QueryTests{ 48 | { 49 | ExpectedType: "ec2-instance", 50 | ExpectedMethod: sdp.QueryMethod_GET, 51 | ExpectedQuery: "i-0337802d908b4a81e", 52 | ExpectedScope: "foo", 53 | }, 54 | } 55 | 56 | tests.Execute(t, item) 57 | } 58 | -------------------------------------------------------------------------------- /aws-source/adapters/iam-group_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/aws" 8 | "github.com/aws/aws-sdk-go-v2/service/iam" 9 | "github.com/aws/aws-sdk-go-v2/service/iam/types" 10 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 11 | ) 12 | 13 | func TestGroupItemMapper(t *testing.T) { 14 | zone := types.Group{ 15 | Path: adapterhelpers.PtrString("/"), 16 | GroupName: adapterhelpers.PtrString("power-users"), 17 | GroupId: adapterhelpers.PtrString("AGPA3VLV2U27T6SSLJMDS"), 18 | Arn: adapterhelpers.PtrString("arn:aws:iam::801795385023:group/power-users"), 19 | CreateDate: adapterhelpers.PtrTime(time.Now()), 20 | } 21 | 22 | item, err := groupItemMapper(nil, "foo", &zone) 23 | 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | 28 | if err = item.Validate(); err != nil { 29 | t.Error(err) 30 | } 31 | 32 | } 33 | 34 | func TestNewIAMGroupAdapter(t *testing.T) { 35 | config, account, _ := adapterhelpers.GetAutoConfig(t) 36 | client := iam.NewFromConfig(config, func(o *iam.Options) { 37 | o.RetryMode = aws.RetryModeAdaptive 38 | o.RetryMaxAttempts = 10 39 | }) 40 | 41 | adapter := NewIAMGroupAdapter(client, account) 42 | 43 | test := adapterhelpers.E2ETest{ 44 | Adapter: adapter, 45 | Timeout: 30 * time.Second, 46 | } 47 | 48 | test.Run(t) 49 | } 50 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/apigateway/delete.go: -------------------------------------------------------------------------------- 1 | package apigateway 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/apigateway" 7 | 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | ) 10 | 11 | func deleteRestAPI(ctx context.Context, client *apigateway.Client, restAPIID string) error { 12 | _, err := client.DeleteRestApi(ctx, &apigateway.DeleteRestApiInput{ 13 | RestApiId: adapterhelpers.PtrString(restAPIID), 14 | }) 15 | 16 | return err 17 | } 18 | 19 | func deleteAPIKeyByName(ctx context.Context, client *apigateway.Client, id *string) error { 20 | _, err := client.DeleteApiKey(ctx, &apigateway.DeleteApiKeyInput{ 21 | ApiKey: id, 22 | }) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/apigateway/teardown.go: -------------------------------------------------------------------------------- 1 | package apigateway 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "log/slog" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/apigateway" 9 | 10 | "github.com/overmindtech/cli/aws-source/adapters/integration" 11 | ) 12 | 13 | func teardown(ctx context.Context, logger *slog.Logger, client *apigateway.Client) error { 14 | restAPIID, err := findRestAPIsByTags(ctx, client) 15 | if err != nil { 16 | if nf := integration.NewNotFoundError(restAPISrc); errors.As(err, &nf) { 17 | logger.WarnContext(ctx, "Rest API not found") 18 | } else { 19 | return err 20 | } 21 | } else { 22 | err = deleteRestAPI(ctx, client, *restAPIID) 23 | if err != nil { 24 | return err 25 | } 26 | } 27 | 28 | keyName := integration.ResourceName(integration.APIGateway, apiKeySrc, integration.TestID()) 29 | apiKeyID, err := findAPIKeyByName(ctx, client, keyName) 30 | if err != nil { 31 | if nf := integration.NewNotFoundError(apiKeySrc); errors.As(err, &nf) { 32 | logger.WarnContext(ctx, "API Key not found", "name", keyName) 33 | return nil 34 | } else { 35 | return err 36 | } 37 | } else { 38 | err = deleteAPIKeyByName(ctx, client, apiKeyID) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/apigateway/util.go: -------------------------------------------------------------------------------- 1 | package apigateway 2 | 3 | import ( 4 | "github.com/overmindtech/cli/aws-source/adapters/integration" 5 | ) 6 | 7 | func resourceTags(resourceName, testID string, nameAdditionalAttr ...string) map[string]string { 8 | return map[string]string{ 9 | integration.TagTestKey: integration.TagTestValue, 10 | integration.TagTestTypeKey: integration.TestName(integration.APIGateway), 11 | integration.TagTestIDKey: testID, 12 | integration.TagResourceIDKey: integration.ResourceName(integration.APIGateway, resourceName, nameAdditionalAttr...), 13 | } 14 | } 15 | 16 | func hasTags(tags map[string]string, requiredTags map[string]string) bool { 17 | for k, v := range requiredTags { 18 | if tags[k] != v { 19 | return false 20 | } 21 | } 22 | 23 | return true 24 | } 25 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/ec2/delete.go: -------------------------------------------------------------------------------- 1 | package ec2 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/ec2" 7 | ) 8 | 9 | func deleteInstance(ctx context.Context, client *ec2.Client, instanceID string) error { 10 | input := &ec2.TerminateInstancesInput{ 11 | InstanceIds: []string{instanceID}, 12 | } 13 | 14 | _, err := client.TerminateInstances(ctx, input) 15 | return err 16 | } 17 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/ec2/find.go: -------------------------------------------------------------------------------- 1 | package ec2 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/ec2" 7 | "github.com/aws/aws-sdk-go-v2/service/ec2/types" 8 | "github.com/overmindtech/cli/aws-source/adapters/integration" 9 | ) 10 | 11 | // findActiveInstanceIDByTags finds an instance by tags 12 | // additionalAttr is a variadic parameter that allows to specify additional attributes to search for 13 | // it ignores terminated instances 14 | func findActiveInstanceIDByTags(ctx context.Context, client *ec2.Client, additionalAttr ...string) (*string, error) { 15 | result, err := client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{}) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | for _, reservation := range result.Reservations { 21 | for _, instance := range reservation.Instances { 22 | // ignore terminated or shutting down instances 23 | if instance.State.Name == types.InstanceStateNameTerminated || 24 | instance.State.Name == types.InstanceStateNameShuttingDown { 25 | // ignore terminated instances 26 | continue 27 | } 28 | 29 | if hasTags(instance.Tags, resourceTags(instanceSrc, integration.TestID(), additionalAttr...)) { 30 | return instance.InstanceId, nil 31 | } 32 | } 33 | } 34 | 35 | return nil, integration.NewNotFoundError(integration.ResourceName(integration.EC2, instanceSrc, additionalAttr...)) 36 | } 37 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/ec2/main_test.go: -------------------------------------------------------------------------------- 1 | package ec2 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log/slog" 7 | "os" 8 | "testing" 9 | 10 | awsec2 "github.com/aws/aws-sdk-go-v2/service/ec2" 11 | "github.com/overmindtech/cli/aws-source/adapters/integration" 12 | ) 13 | 14 | func TestMain(m *testing.M) { 15 | if integration.ShouldRunIntegrationTests() { 16 | fmt.Println("Running integration tests") 17 | os.Exit(m.Run()) 18 | } else { 19 | fmt.Println("Skipping integration tests, set RUN_INTEGRATION_TESTS=true to run them") 20 | os.Exit(0) 21 | } 22 | } 23 | 24 | func TestIntegrationEC2(t *testing.T) { 25 | t.Run("Setup", Setup) 26 | t.Run("EC2", EC2) 27 | t.Run("Teardown", Teardown) 28 | } 29 | 30 | func Setup(t *testing.T) { 31 | ctx := context.Background() 32 | logger := slog.Default() 33 | 34 | var err error 35 | testClient, err := ec2Client(ctx) 36 | if err != nil { 37 | t.Fatalf("Failed to create EC2 client: %v", err) 38 | } 39 | 40 | if err := setup(ctx, logger, testClient); err != nil { 41 | t.Fatalf("Failed to setup EC2 integration tests: %v", err) 42 | } 43 | } 44 | 45 | func Teardown(t *testing.T) { 46 | ctx := context.Background() 47 | logger := slog.Default() 48 | 49 | var err error 50 | testClient, err := ec2Client(ctx) 51 | if err != nil { 52 | t.Fatalf("Failed to create EC2 client: %v", err) 53 | } 54 | 55 | if err := teardown(ctx, logger, testClient); err != nil { 56 | t.Fatalf("Failed to teardown EC2 integration tests: %v", err) 57 | } 58 | } 59 | 60 | func ec2Client(ctx context.Context) (*awsec2.Client, error) { 61 | testAWSConfig, err := integration.AWSSettings(ctx) 62 | if err != nil { 63 | return nil, fmt.Errorf("failed to get AWS settings: %w", err) 64 | } 65 | 66 | return awsec2.NewFromConfig(testAWSConfig.Config), nil 67 | } 68 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/ec2/setup.go: -------------------------------------------------------------------------------- 1 | package ec2 2 | 3 | import ( 4 | "context" 5 | "log/slog" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/ec2" 8 | "github.com/overmindtech/cli/aws-source/adapters/integration" 9 | ) 10 | 11 | const instanceSrc = "instance" 12 | 13 | func setup(ctx context.Context, logger *slog.Logger, client *ec2.Client) error { 14 | // Create EC2 instance 15 | return createEC2Instance(ctx, logger, client, integration.TestID()) 16 | } 17 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/ec2/teardown.go: -------------------------------------------------------------------------------- 1 | package ec2 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "log/slog" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/ec2" 9 | "github.com/overmindtech/cli/aws-source/adapters/integration" 10 | ) 11 | 12 | func teardown(ctx context.Context, logger *slog.Logger, client *ec2.Client) error { 13 | instanceID, err := findActiveInstanceIDByTags(ctx, client) 14 | if err != nil { 15 | nf := integration.NewNotFoundError(instanceSrc) 16 | if errors.As(err, &nf) { 17 | logger.WarnContext(ctx, "Instance not found") 18 | return nil 19 | } else { 20 | return err 21 | } 22 | } 23 | 24 | return deleteInstance(ctx, client, *instanceID) 25 | } 26 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/ec2/util.go: -------------------------------------------------------------------------------- 1 | package ec2 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go-v2/aws" 5 | "github.com/aws/aws-sdk-go-v2/service/ec2/types" 6 | "github.com/overmindtech/cli/aws-source/adapters/integration" 7 | ) 8 | 9 | func resourceTags(resourceName, testID string, nameAdditionalAttr ...string) []types.Tag { 10 | return []types.Tag{ 11 | { 12 | Key: aws.String(integration.TagTestKey), 13 | Value: aws.String(integration.TagTestValue), 14 | }, 15 | { 16 | Key: aws.String(integration.TagTestTypeKey), 17 | Value: aws.String(integration.TestName(integration.EC2)), 18 | }, 19 | { 20 | Key: aws.String(integration.TagTestIDKey), 21 | Value: aws.String(testID), 22 | }, 23 | { 24 | Key: aws.String(integration.TagResourceIDKey), 25 | Value: aws.String(integration.ResourceName(integration.EC2, resourceName, nameAdditionalAttr...)), 26 | }, 27 | } 28 | } 29 | 30 | func hasTags(tags []types.Tag, requiredTags []types.Tag) bool { 31 | rT := make(map[string]string) 32 | for _, t := range requiredTags { 33 | rT[*t.Key] = *t.Value 34 | } 35 | 36 | oT := make(map[string]string) 37 | for _, t := range tags { 38 | oT[*t.Key] = *t.Value 39 | } 40 | 41 | for k, v := range rT { 42 | if oT[k] != v { 43 | return false 44 | } 45 | } 46 | 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/errors.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import "fmt" 4 | 5 | type NotFoundError struct { 6 | ResourceName string 7 | } 8 | 9 | func (e NotFoundError) Error() string { 10 | return fmt.Sprintf("Resource not found: %s", e.ResourceName) 11 | } 12 | 13 | func NewNotFoundError(resourceName string) NotFoundError { 14 | return NotFoundError{ResourceName: resourceName} 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/kms/delete.go: -------------------------------------------------------------------------------- 1 | package kms 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/kms" 7 | ) 8 | 9 | func deleteKey(ctx context.Context, client *kms.Client, keyID string) error { 10 | seven := int32(7) 11 | _, err := client.ScheduleKeyDeletion(ctx, &kms.ScheduleKeyDeletionInput{ 12 | KeyId: &keyID, 13 | PendingWindowInDays: &seven, // it can be minimum 7 days 14 | }) 15 | return err 16 | } 17 | 18 | func deleteAlias(ctx context.Context, client *kms.Client, aliasName string) error { 19 | _, err := client.DeleteAlias(ctx, &kms.DeleteAliasInput{ 20 | AliasName: &aliasName, 21 | }) 22 | return err 23 | } 24 | 25 | func deleteGrant(ctx context.Context, client *kms.Client, keyID, grantID string) error { 26 | _, err := client.RevokeGrant(ctx, &kms.RevokeGrantInput{ 27 | KeyId: &keyID, 28 | GrantId: &grantID, 29 | }) 30 | return err 31 | } 32 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/kms/main_test.go: -------------------------------------------------------------------------------- 1 | package kms 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log/slog" 7 | "os" 8 | "testing" 9 | 10 | awskms "github.com/aws/aws-sdk-go-v2/service/kms" 11 | "github.com/overmindtech/cli/aws-source/adapters/integration" 12 | ) 13 | 14 | func TestMain(m *testing.M) { 15 | if integration.ShouldRunIntegrationTests() { 16 | fmt.Println("Running integration tests") 17 | os.Exit(m.Run()) 18 | } else { 19 | fmt.Println("Skipping integration tests, set RUN_INTEGRATION_TESTS=true to run them") 20 | os.Exit(0) 21 | } 22 | } 23 | 24 | func TestIntegrationKMS(t *testing.T) { 25 | t.Run("Setup", Setup) 26 | t.Run("KMS", KMS) 27 | t.Run("Teardown", Teardown) 28 | } 29 | 30 | func Setup(t *testing.T) { 31 | ctx := context.Background() 32 | logger := slog.Default() 33 | 34 | var err error 35 | testClient, err := kmsClient(ctx) 36 | if err != nil { 37 | t.Fatalf("Failed to create KMS client: %v", err) 38 | } 39 | 40 | if err := setup(ctx, logger, testClient); err != nil { 41 | t.Fatalf("Failed to setup KMS integration tests: %v", err) 42 | } 43 | } 44 | 45 | func Teardown(t *testing.T) { 46 | ctx := context.Background() 47 | logger := slog.Default() 48 | 49 | var err error 50 | testClient, err := kmsClient(ctx) 51 | if err != nil { 52 | t.Fatalf("Failed to create KMS client: %v", err) 53 | } 54 | 55 | if err := teardown(ctx, logger, testClient); err != nil { 56 | t.Fatalf("Failed to teardown KMS integration tests: %v", err) 57 | } 58 | } 59 | 60 | func kmsClient(ctx context.Context) (*awskms.Client, error) { 61 | testAWSConfig, err := integration.AWSSettings(ctx) 62 | if err != nil { 63 | return nil, fmt.Errorf("failed to get AWS settings: %w", err) 64 | } 65 | 66 | return awskms.NewFromConfig(testAWSConfig.Config), nil 67 | } 68 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/kms/setup.go: -------------------------------------------------------------------------------- 1 | package kms 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log/slog" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/kms" 9 | "github.com/overmindtech/cli/aws-source/adapters/integration" 10 | ) 11 | 12 | const ( 13 | keySrc = "key" 14 | aliasSrc = "alias" 15 | grantSrc = "grant" 16 | keyPolicySrc = "key-policy" 17 | ) 18 | 19 | func setup(ctx context.Context, logger *slog.Logger, client *kms.Client) error { 20 | testID := integration.TestID() 21 | 22 | // Create KMS key 23 | keyID, err := createKey(ctx, logger, client, testID) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | // Create KMS alias 29 | err = createAlias(ctx, logger, client, *keyID) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | principal, err := integration.GetCallerIdentityARN(ctx) 35 | if err != nil { 36 | return fmt.Errorf("failed to get caller identity: %w", err) 37 | } 38 | 39 | // Create KMS grant 40 | err = createGrant(ctx, logger, client, *keyID, *principal) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | // Create KMS key policy 46 | return putKeyPolicy(ctx, logger, client, *keyID, *principal) 47 | } 48 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/kms/teardown.go: -------------------------------------------------------------------------------- 1 | package kms 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "log/slog" 8 | 9 | "github.com/aws/aws-sdk-go-v2/service/kms" 10 | "github.com/overmindtech/cli/aws-source/adapters/integration" 11 | ) 12 | 13 | func teardown(ctx context.Context, logger *slog.Logger, client *kms.Client) error { 14 | keyID, err := findActiveKeyIDByTags(ctx, client) 15 | if err != nil { 16 | if nf := integration.NewNotFoundError(keySrc); errors.As(err, &nf) { 17 | logger.WarnContext(ctx, "Key not found") 18 | return nil 19 | } else { 20 | return err 21 | } 22 | } 23 | 24 | principal, err := integration.GetCallerIdentityARN(ctx) 25 | if err != nil { 26 | return fmt.Errorf("failed to get caller identity: %w", err) 27 | } 28 | 29 | grantID, err := findGrant(ctx, client, *keyID, *principal) 30 | if err != nil { 31 | if nf := integration.NewNotFoundError(grantSrc); errors.As(err, &nf) { 32 | logger.WarnContext(ctx, "Grant not found") 33 | } else { 34 | return err 35 | } 36 | } 37 | 38 | err = deleteGrant(ctx, client, *keyID, *grantID) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | aliasNames, err := findAliasesByTargetKey(ctx, client, *keyID) 44 | if err != nil { 45 | if nf := integration.NewNotFoundError(aliasSrc); errors.As(err, &nf) { 46 | logger.WarnContext(ctx, "Alias not found") 47 | } else { 48 | return err 49 | } 50 | } 51 | 52 | for _, aliasName := range aliasNames { 53 | err = deleteAlias(ctx, client, aliasName) 54 | if err != nil { 55 | return err 56 | } 57 | } 58 | 59 | return deleteKey(ctx, client, *keyID) 60 | } 61 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/kms/util.go: -------------------------------------------------------------------------------- 1 | package kms 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go-v2/aws" 5 | "github.com/aws/aws-sdk-go-v2/service/kms/types" 6 | "github.com/overmindtech/cli/aws-source/adapters/integration" 7 | ) 8 | 9 | func resourceTags(resourceName, testID string, nameAdditionalAttr ...string) []types.Tag { 10 | return []types.Tag{ 11 | { 12 | TagKey: aws.String(integration.TagTestKey), 13 | TagValue: aws.String(integration.TagTestValue), 14 | }, 15 | { 16 | TagKey: aws.String(integration.TagTestTypeKey), 17 | TagValue: aws.String(integration.TestName(integration.KMS)), 18 | }, 19 | { 20 | TagKey: aws.String(integration.TagTestIDKey), 21 | TagValue: aws.String(testID), 22 | }, 23 | { 24 | TagKey: aws.String(integration.TagResourceIDKey), 25 | TagValue: aws.String(integration.ResourceName(integration.KMS, resourceName, nameAdditionalAttr...)), 26 | }, 27 | } 28 | } 29 | 30 | func hasTags(tags []types.Tag, requiredTags []types.Tag) bool { 31 | rT := make(map[string]string) 32 | for _, t := range requiredTags { 33 | rT[*t.TagKey] = *t.TagValue 34 | } 35 | 36 | oT := make(map[string]string) 37 | for _, t := range tags { 38 | oT[*t.TagKey] = *t.TagValue 39 | } 40 | 41 | for k, v := range rT { 42 | if oT[k] != v { 43 | return false 44 | } 45 | } 46 | 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/networkmanager/tags.go: -------------------------------------------------------------------------------- 1 | package networkmanager 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go-v2/aws" 5 | "github.com/aws/aws-sdk-go-v2/service/networkmanager/types" 6 | "github.com/overmindtech/cli/aws-source/adapters/integration" 7 | ) 8 | 9 | func resourceTags(resourceName, testID string, additionalAttr ...string) []types.Tag { 10 | return []types.Tag{ 11 | { 12 | Key: aws.String(integration.TagTestKey), 13 | Value: aws.String(integration.TagTestValue), 14 | }, 15 | { 16 | Key: aws.String(integration.TagTestTypeKey), 17 | Value: aws.String(integration.TestName(integration.NetworkManager)), 18 | }, 19 | { 20 | Key: aws.String(integration.TagTestIDKey), 21 | Value: aws.String(testID), 22 | }, 23 | { 24 | Key: aws.String(integration.TagResourceIDKey), 25 | Value: aws.String(integration.ResourceName(integration.NetworkManager, resourceName, additionalAttr...)), 26 | }, 27 | } 28 | } 29 | 30 | func hasTags(tags []types.Tag, requiredTags []types.Tag) bool { 31 | rT := make(map[string]string) 32 | for _, t := range requiredTags { 33 | rT[*t.Key] = *t.Value 34 | } 35 | 36 | oT := make(map[string]string) 37 | for _, t := range tags { 38 | oT[*t.Key] = *t.Value 39 | } 40 | 41 | for k, v := range rT { 42 | if oT[k] != v { 43 | return false 44 | } 45 | } 46 | 47 | return true 48 | } 49 | -------------------------------------------------------------------------------- /aws-source/adapters/integration/util_test.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func Test_testID(t *testing.T) { 9 | t.Run("test id is given via env var", func(t *testing.T) { 10 | err := os.Setenv("INTEGRATION_TEST_ID", "test-id") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | defer func() { 15 | err := os.Unsetenv("INTEGRATION_TEST_ID") 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | }() 20 | 21 | if got := TestID(); got != "test-id" { 22 | t.Errorf("TestID() = %v, want %v", got, "test-id") 23 | } 24 | }) 25 | 26 | t.Run("test id is not given via env var - defaults to host name", func(t *testing.T) { 27 | err := os.Unsetenv("INTEGRATION_TEST_ID") 28 | if err != nil { 29 | t.Error(err) 30 | } 31 | 32 | if got := TestID(); got == "" { 33 | t.Errorf("TestID() = %v, want not empty", got) 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /aws-source/adapters/kms-alias_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | "github.com/overmindtech/cli/sdp-go" 10 | 11 | "github.com/aws/aws-sdk-go-v2/service/kms" 12 | "github.com/aws/aws-sdk-go-v2/service/kms/types" 13 | ) 14 | 15 | func TestAliasOutputMapper(t *testing.T) { 16 | output := &kms.ListAliasesOutput{ 17 | Aliases: []types.AliasListEntry{ 18 | { 19 | AliasName: adapterhelpers.PtrString("alias/test-key"), 20 | TargetKeyId: adapterhelpers.PtrString("cf68415c-f4ae-48f2-87a7-3b52ce"), 21 | AliasArn: adapterhelpers.PtrString("arn:aws:kms:us-west-2:123456789012:alias/test-key"), 22 | CreationDate: adapterhelpers.PtrTime(time.Now()), 23 | LastUpdatedDate: adapterhelpers.PtrTime(time.Now()), 24 | }, 25 | }, 26 | } 27 | 28 | items, err := aliasOutputMapper(context.Background(), nil, "foo", nil, output) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | for _, item := range items { 34 | if err := item.Validate(); err != nil { 35 | t.Error(err) 36 | } 37 | } 38 | 39 | if len(items) != 1 { 40 | t.Fatalf("expected 1 item, got %v", len(items)) 41 | } 42 | 43 | item := items[0] 44 | 45 | tests := adapterhelpers.QueryTests{ 46 | { 47 | ExpectedType: "kms-key", 48 | ExpectedMethod: sdp.QueryMethod_GET, 49 | ExpectedQuery: "cf68415c-f4ae-48f2-87a7-3b52ce", 50 | ExpectedScope: "foo", 51 | }, 52 | } 53 | 54 | tests.Execute(t, item) 55 | } 56 | 57 | func TestNewKMSAliasAdapter(t *testing.T) { 58 | config, account, region := adapterhelpers.GetAutoConfig(t) 59 | client := kms.NewFromConfig(config) 60 | 61 | adapter := NewKMSAliasAdapter(client, account, region) 62 | 63 | test := adapterhelpers.E2ETest{ 64 | Adapter: adapter, 65 | Timeout: 10 * time.Second, 66 | } 67 | 68 | test.Run(t) 69 | } 70 | -------------------------------------------------------------------------------- /aws-source/adapters/kms.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/kms" 7 | "github.com/aws/aws-sdk-go-v2/service/kms/types" 8 | ) 9 | 10 | func kmsTags(ctx context.Context, cli kmsClient, keyID string) (map[string]string, error) { 11 | if cli == nil { 12 | return nil, nil 13 | } 14 | 15 | output, err := cli.ListResourceTags(ctx, &kms.ListResourceTagsInput{ 16 | KeyId: &keyID, 17 | }) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return kmsTagsToMap(output.Tags), nil 23 | } 24 | 25 | func kmsTagsToMap(tags []types.Tag) map[string]string { 26 | tagsMap := make(map[string]string) 27 | 28 | for _, tag := range tags { 29 | if tag.TagKey != nil && tag.TagValue != nil { 30 | tagsMap[*tag.TagKey] = *tag.TagValue 31 | } 32 | } 33 | 34 | return tagsMap 35 | } 36 | -------------------------------------------------------------------------------- /aws-source/adapters/lambda-layer_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/lambda/types" 8 | 9 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 10 | "github.com/overmindtech/cli/sdp-go" 11 | ) 12 | 13 | func TestLayerItemMapper(t *testing.T) { 14 | layer := types.LayersListItem{ 15 | LatestMatchingVersion: &types.LayerVersionsListItem{ 16 | CompatibleArchitectures: []types.Architecture{ 17 | types.ArchitectureArm64, 18 | types.ArchitectureX8664, 19 | }, 20 | CompatibleRuntimes: []types.Runtime{ 21 | types.RuntimeJava11, 22 | }, 23 | CreatedDate: adapterhelpers.PtrString("2018-11-27T15:10:45.123+0000"), 24 | Description: adapterhelpers.PtrString("description"), 25 | LayerVersionArn: adapterhelpers.PtrString("arn:aws:service:region:account:type/id"), 26 | LicenseInfo: adapterhelpers.PtrString("info"), 27 | Version: 10, 28 | }, 29 | LayerArn: adapterhelpers.PtrString("arn:aws:service:region:account:type/id"), 30 | LayerName: adapterhelpers.PtrString("name"), 31 | } 32 | 33 | item, err := layerItemMapper("", "foo", &layer) 34 | 35 | if err != nil { 36 | t.Error(err) 37 | } 38 | 39 | if err = item.Validate(); err != nil { 40 | t.Error(err) 41 | } 42 | 43 | tests := adapterhelpers.QueryTests{ 44 | { 45 | ExpectedType: "lambda-layer-version", 46 | ExpectedMethod: sdp.QueryMethod_GET, 47 | ExpectedQuery: "name:10", 48 | ExpectedScope: "foo", 49 | }, 50 | } 51 | 52 | tests.Execute(t, item) 53 | } 54 | 55 | func TestNewLambdaLayerAdapter(t *testing.T) { 56 | client, account, region := lambdaGetAutoConfig(t) 57 | 58 | adapter := NewLambdaLayerAdapter(client, account, region) 59 | 60 | test := adapterhelpers.E2ETest{ 61 | Adapter: adapter, 62 | Timeout: 10 * time.Second, 63 | SkipGet: true, 64 | } 65 | 66 | test.Run(t) 67 | } 68 | -------------------------------------------------------------------------------- /aws-source/adapters/lambda.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/lambda" 7 | ) 8 | 9 | // LambdaClient Represents the client we need to talk to Lambda, usually this is 10 | // *lambda.Client 11 | type LambdaClient interface { 12 | GetFunction(ctx context.Context, params *lambda.GetFunctionInput, optFns ...func(*lambda.Options)) (*lambda.GetFunctionOutput, error) 13 | GetLayerVersion(ctx context.Context, params *lambda.GetLayerVersionInput, optFns ...func(*lambda.Options)) (*lambda.GetLayerVersionOutput, error) 14 | GetPolicy(ctx context.Context, params *lambda.GetPolicyInput, optFns ...func(*lambda.Options)) (*lambda.GetPolicyOutput, error) 15 | 16 | lambda.ListFunctionEventInvokeConfigsAPIClient 17 | lambda.ListFunctionUrlConfigsAPIClient 18 | lambda.ListFunctionsAPIClient 19 | lambda.ListLayerVersionsAPIClient 20 | } 21 | 22 | // This is derived from the AWS example: 23 | // https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/gov2/iam/actions/policies.go#L21C1-L32C2 24 | // and represents the structure of an IAM policy document 25 | type PolicyDocument struct { 26 | Version string `json:""` 27 | Statement []PolicyStatement `json:""` 28 | } 29 | 30 | // PolicyStatement defines a statement in a policy document. 31 | type PolicyStatement struct { 32 | Action string 33 | Principal Principal `json:",omitempty"` 34 | Condition Condition `json:",omitempty"` 35 | } 36 | 37 | type Principal struct { 38 | Service string `json:",omitempty"` 39 | } 40 | 41 | type Condition struct { 42 | ArnLike ArnLikeCondition `json:",omitempty"` 43 | StringEquals StringEqualsCondition `json:",omitempty"` 44 | } 45 | 46 | type StringEqualsCondition struct { 47 | AWSSourceAccount string `json:"AWS:SourceAccount,omitempty"` 48 | } 49 | 50 | type ArnLikeCondition struct { 51 | AWSSourceArn string `json:"AWS:SourceArn,omitempty"` 52 | } 53 | -------------------------------------------------------------------------------- /aws-source/adapters/main.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/sdp-go" 5 | ) 6 | 7 | var Metadata = sdp.AdapterMetadataList{} 8 | -------------------------------------------------------------------------------- /aws-source/adapters/networkfirewall_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | type testNetworkFirewallClient struct{} 4 | -------------------------------------------------------------------------------- /aws-source/adapters/networkmanager-connect-attachment_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/networkmanager/types" 7 | 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | "github.com/overmindtech/cli/sdp-go" 10 | ) 11 | 12 | func TestConnectAttachmentItemMapper(t *testing.T) { 13 | 14 | scope := "123456789012.eu-west-2" 15 | item, err := connectAttachmentItemMapper("", scope, &types.ConnectAttachment{ 16 | Attachment: &types.Attachment{ 17 | AttachmentId: adapterhelpers.PtrString("att-1"), 18 | CoreNetworkId: adapterhelpers.PtrString("cn-1"), 19 | CoreNetworkArn: adapterhelpers.PtrString("arn:aws:networkmanager:eu-west-2:123456789012:core-network/cn-1"), 20 | }, 21 | }) 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | 26 | // Ensure unique attribute 27 | err = item.Validate() 28 | if err != nil { 29 | t.Error(err) 30 | } 31 | 32 | if item.UniqueAttributeValue() != "att-1" { 33 | t.Fatalf("expected att-1, got %v", item.UniqueAttributeValue()) 34 | } 35 | 36 | tests := adapterhelpers.QueryTests{ 37 | { 38 | ExpectedType: "networkmanager-core-network", 39 | ExpectedMethod: sdp.QueryMethod_GET, 40 | ExpectedQuery: "cn-1", 41 | ExpectedScope: scope, 42 | }, 43 | { 44 | ExpectedType: "networkmanager-core-network", 45 | ExpectedMethod: sdp.QueryMethod_SEARCH, 46 | ExpectedQuery: "arn:aws:networkmanager:eu-west-2:123456789012:core-network/cn-1", 47 | ExpectedScope: "123456789012.eu-west-2", 48 | }, 49 | } 50 | 51 | tests.Execute(t, item) 52 | } 53 | -------------------------------------------------------------------------------- /aws-source/adapters/networkmanager-core-network-policy_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/networkmanager/types" 7 | 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | "github.com/overmindtech/cli/sdp-go" 10 | ) 11 | 12 | func TestCoreNetworkPolicyItemMapper(t *testing.T) { 13 | 14 | scope := "123456789012.eu-west-2" 15 | item, err := coreNetworkPolicyItemMapper("", scope, &types.CoreNetworkPolicy{ 16 | CoreNetworkId: adapterhelpers.PtrString("cn-1"), 17 | PolicyVersionId: adapterhelpers.PtrInt32(1), 18 | }) 19 | if err != nil { 20 | t.Error(err) 21 | } 22 | 23 | // Ensure unique attribute 24 | err = item.Validate() 25 | if err != nil { 26 | t.Error(err) 27 | } 28 | 29 | if item.UniqueAttributeValue() != "cn-1" { 30 | t.Fatalf("expected cn-1, got %v", item.UniqueAttributeValue()) 31 | } 32 | 33 | tests := adapterhelpers.QueryTests{ 34 | { 35 | ExpectedType: "networkmanager-core-network", 36 | ExpectedMethod: sdp.QueryMethod_GET, 37 | ExpectedQuery: "cn-1", 38 | ExpectedScope: scope, 39 | }, 40 | } 41 | 42 | tests.Execute(t, item) 43 | } 44 | -------------------------------------------------------------------------------- /aws-source/adapters/networkmanager-vpc-attachment_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/networkmanager/types" 7 | 8 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 9 | "github.com/overmindtech/cli/sdp-go" 10 | ) 11 | 12 | func TestVPCAttachmentItemMapper(t *testing.T) { 13 | input := types.VpcAttachment{ 14 | Attachment: &types.Attachment{ 15 | AttachmentId: adapterhelpers.PtrString("attachment1"), 16 | CoreNetworkId: adapterhelpers.PtrString("corenetwork1"), 17 | }, 18 | } 19 | scope := "123456789012.eu-west-2" 20 | item, err := vpcAttachmentItemMapper("", scope, &input) 21 | 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | if err := item.Validate(); err != nil { 26 | t.Error(err) 27 | } 28 | 29 | // Ensure unique attribute 30 | if item.UniqueAttributeValue() != "attachment1" { 31 | t.Fatalf("expected %v, got %v", "attachment1", item.UniqueAttributeValue()) 32 | } 33 | 34 | tests := adapterhelpers.QueryTests{ 35 | { 36 | ExpectedType: "networkmanager-core-network", 37 | ExpectedMethod: sdp.QueryMethod_GET, 38 | ExpectedQuery: "corenetwork1", 39 | ExpectedScope: scope, 40 | }, 41 | } 42 | 43 | tests.Execute(t, item) 44 | } 45 | -------------------------------------------------------------------------------- /aws-source/adapters/networkmanager.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/networkmanager" 7 | "github.com/aws/aws-sdk-go-v2/service/networkmanager/types" 8 | ) 9 | 10 | type NetworkManagerClient interface { 11 | networkmanager.ListConnectPeersAPIClient 12 | networkmanager.ListCoreNetworksAPIClient 13 | 14 | GetConnectPeer(ctx context.Context, params *networkmanager.GetConnectPeerInput, optFns ...func(*networkmanager.Options)) (*networkmanager.GetConnectPeerOutput, error) 15 | GetCoreNetwork(ctx context.Context, params *networkmanager.GetCoreNetworkInput, optFns ...func(*networkmanager.Options)) (*networkmanager.GetCoreNetworkOutput, error) 16 | } 17 | 18 | // convertTags converts slice of ecs tags to a map 19 | func networkmanagerTagsToMap(tags []types.Tag) map[string]string { 20 | tagsMap := make(map[string]string) 21 | 22 | for _, tag := range tags { 23 | if tag.Key != nil && tag.Value != nil { 24 | tagsMap[*tag.Key] = *tag.Value 25 | } 26 | } 27 | 28 | return tagsMap 29 | } 30 | -------------------------------------------------------------------------------- /aws-source/adapters/networkmanager_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | type NetworkManagerTestClient struct{} 4 | -------------------------------------------------------------------------------- /aws-source/adapters/rds-option-group_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/rds" 8 | "github.com/aws/aws-sdk-go-v2/service/rds/types" 9 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 10 | ) 11 | 12 | func TestOptionGroupOutputMapper(t *testing.T) { 13 | output := rds.DescribeOptionGroupsOutput{ 14 | OptionGroupsList: []types.OptionGroup{ 15 | { 16 | OptionGroupName: adapterhelpers.PtrString("default:aurora-mysql-8-0"), 17 | OptionGroupDescription: adapterhelpers.PtrString("Default option group for aurora-mysql 8.0"), 18 | EngineName: adapterhelpers.PtrString("aurora-mysql"), 19 | MajorEngineVersion: adapterhelpers.PtrString("8.0"), 20 | Options: []types.Option{}, 21 | AllowsVpcAndNonVpcInstanceMemberships: adapterhelpers.PtrBool(true), 22 | OptionGroupArn: adapterhelpers.PtrString("arn:aws:rds:eu-west-2:052392120703:og:default:aurora-mysql-8-0"), 23 | }, 24 | }, 25 | } 26 | 27 | items, err := optionGroupOutputMapper(context.Background(), mockRdsClient{}, "foo", nil, &output) 28 | 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | if len(items) != 1 { 34 | t.Fatalf("got %v items, expected 1", len(items)) 35 | } 36 | 37 | item := items[0] 38 | 39 | if err = item.Validate(); err != nil { 40 | t.Error(err) 41 | } 42 | 43 | if item.GetTags()["key"] != "value" { 44 | t.Errorf("expected key to be value, got %v", item.GetTags()["key"]) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /aws-source/adapters/rds_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/rds" 7 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 8 | ) 9 | 10 | func rdsGetAutoConfig(t *testing.T) (*rds.Client, string, string) { 11 | config, account, region := adapterhelpers.GetAutoConfig(t) 12 | client := rds.NewFromConfig(config) 13 | 14 | return client, account, region 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/route53-hosted-zone_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/aws/aws-sdk-go-v2/service/route53/types" 8 | 9 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 10 | "github.com/overmindtech/cli/sdp-go" 11 | ) 12 | 13 | func TestHostedZoneItemMapper(t *testing.T) { 14 | zone := types.HostedZone{ 15 | Id: adapterhelpers.PtrString("/hostedzone/Z08416862SZP5DJXIDB29"), 16 | Name: adapterhelpers.PtrString("overmind-demo.com."), 17 | CallerReference: adapterhelpers.PtrString("RISWorkflow-RD:144d3779-1574-42bf-9e75-f309838ea0a1"), 18 | Config: &types.HostedZoneConfig{ 19 | Comment: adapterhelpers.PtrString("HostedZone created by Route53 Registrar"), 20 | PrivateZone: false, 21 | }, 22 | ResourceRecordSetCount: adapterhelpers.PtrInt64(3), 23 | LinkedService: &types.LinkedService{ 24 | Description: adapterhelpers.PtrString("service description"), 25 | ServicePrincipal: adapterhelpers.PtrString("principal"), 26 | }, 27 | } 28 | 29 | item, err := hostedZoneItemMapper("", "foo", &zone) 30 | 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | 35 | if err = item.Validate(); err != nil { 36 | t.Error(err) 37 | } 38 | 39 | tests := adapterhelpers.QueryTests{ 40 | { 41 | ExpectedType: "route53-resource-record-set", 42 | ExpectedMethod: sdp.QueryMethod_SEARCH, 43 | ExpectedQuery: "/hostedzone/Z08416862SZP5DJXIDB29", 44 | ExpectedScope: "foo", 45 | }, 46 | } 47 | 48 | tests.Execute(t, item) 49 | } 50 | 51 | func TestNewRoute53HostedZoneAdapter(t *testing.T) { 52 | client, account, region := route53GetAutoConfig(t) 53 | 54 | adapter := NewRoute53HostedZoneAdapter(client, account, region) 55 | 56 | test := adapterhelpers.E2ETest{ 57 | Adapter: adapter, 58 | Timeout: 10 * time.Second, 59 | } 60 | 61 | test.Run(t) 62 | } 63 | -------------------------------------------------------------------------------- /aws-source/adapters/route53.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import "github.com/aws/aws-sdk-go-v2/service/route53/types" 4 | 5 | func route53TagsToMap(tags []types.Tag) map[string]string { 6 | m := make(map[string]string) 7 | 8 | for _, tag := range tags { 9 | if tag.Key != nil && tag.Value != nil { 10 | m[*tag.Key] = *tag.Value 11 | } 12 | } 13 | 14 | return m 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/route53_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/route53" 7 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 8 | ) 9 | 10 | func route53GetAutoConfig(t *testing.T) (*route53.Client, string, string) { 11 | config, account, region := adapterhelpers.GetAutoConfig(t) 12 | client := route53.NewFromConfig(config) 13 | 14 | return client, account, region 15 | } 16 | -------------------------------------------------------------------------------- /aws-source/adapters/sns-data-protection-policy_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/aws/aws-sdk-go-v2/service/sns" 9 | "github.com/overmindtech/cli/aws-source/adapterhelpers" 10 | ) 11 | 12 | type mockDataProtectionPolicyClient struct{} 13 | 14 | func (m mockDataProtectionPolicyClient) GetDataProtectionPolicy(ctx context.Context, params *sns.GetDataProtectionPolicyInput, optFns ...func(*sns.Options)) (*sns.GetDataProtectionPolicyOutput, error) { 15 | return &sns.GetDataProtectionPolicyOutput{ 16 | DataProtectionPolicy: adapterhelpers.PtrString("{\"Name\":\"data_protection_policy\",\"Description\":\"Example data protection policy\",\"Version\":\"2021-06-01\",\"Statement\":[{\"DataDirection\":\"Inbound\",\"Principal\":[\"*\"],\"DataIdentifier\":[\"arn:aws:dataprotection::aws:data-identifier/CreditCardNumber\"],\"Operation\":{\"Deny\":{}}}]}"), 17 | }, nil 18 | } 19 | 20 | func TestGetDataProtectionPolicyFunc(t *testing.T) { 21 | ctx := context.Background() 22 | cli := &mockDataProtectionPolicyClient{} 23 | 24 | item, err := getDataProtectionPolicyFunc(ctx, cli, "scope", &sns.GetDataProtectionPolicyInput{ 25 | ResourceArn: adapterhelpers.PtrString("arn:aws:sns:us-east-1:123456789012:mytopic"), 26 | }) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | if err = item.Validate(); err != nil { 32 | t.Fatal(err) 33 | } 34 | } 35 | 36 | func TestNewSNSDataProtectionPolicyAdapter(t *testing.T) { 37 | config, account, region := adapterhelpers.GetAutoConfig(t) 38 | client := sns.NewFromConfig(config) 39 | 40 | adapter := NewSNSDataProtectionPolicyAdapter(client, account, region) 41 | 42 | test := adapterhelpers.E2ETest{ 43 | Adapter: adapter, 44 | Timeout: 10 * time.Second, 45 | SkipList: true, 46 | SkipGet: true, 47 | } 48 | 49 | test.Run(t) 50 | } 51 | -------------------------------------------------------------------------------- /aws-source/adapters/sns.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/sns" 7 | "github.com/aws/aws-sdk-go-v2/service/sns/types" 8 | ) 9 | 10 | type tagLister interface { 11 | ListTagsForResource(ctx context.Context, params *sns.ListTagsForResourceInput, optFns ...func(*sns.Options)) (*sns.ListTagsForResourceOutput, error) 12 | } 13 | 14 | // tagsByResourceARN returns the tags for a given resource ARN 15 | func tagsByResourceARN(ctx context.Context, cli tagLister, resourceARN string) ([]types.Tag, error) { 16 | if cli == nil { 17 | return nil, nil 18 | } 19 | 20 | output, err := cli.ListTagsForResource(ctx, &sns.ListTagsForResourceInput{ 21 | ResourceArn: &resourceARN, 22 | }) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | if output != nil && output.Tags != nil { 28 | return output.Tags, nil 29 | } 30 | 31 | return nil, nil 32 | } 33 | 34 | // tagsToMap converts a slice of tags to a map 35 | func tagsToMap(tags []types.Tag) map[string]string { 36 | tagsMap := make(map[string]string) 37 | 38 | for _, tag := range tags { 39 | if tag.Key != nil && tag.Value != nil { 40 | tagsMap[*tag.Key] = *tag.Value 41 | } 42 | } 43 | 44 | return tagsMap 45 | } 46 | -------------------------------------------------------------------------------- /aws-source/adapters/sqs.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/sqs" 7 | ) 8 | 9 | func tags(ctx context.Context, cli sqsClient, queURL string) (map[string]string, error) { 10 | if cli == nil { 11 | return nil, nil 12 | } 13 | 14 | output, err := cli.ListQueueTags(ctx, &sqs.ListQueueTagsInput{ 15 | QueueUrl: &queURL, 16 | }) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return output.Tags, nil 22 | } 23 | -------------------------------------------------------------------------------- /aws-source/adapters/tracing.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "go.opentelemetry.io/otel" 5 | semconv "go.opentelemetry.io/otel/semconv/v1.26.0" 6 | "go.opentelemetry.io/otel/trace" 7 | ) 8 | 9 | const ( 10 | instrumentationName = "github.com/overmindtech/cli/aws-source/adapters" 11 | instrumentationVersion = "0.0.1" 12 | ) 13 | 14 | var tracer = otel.GetTracerProvider().Tracer( 15 | instrumentationName, 16 | trace.WithInstrumentationVersion(instrumentationVersion), 17 | trace.WithSchemaURL(semconv.SchemaURL), 18 | ) 19 | -------------------------------------------------------------------------------- /aws-source/build/package/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the source binary 2 | FROM golang:1.24-alpine AS builder 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | ARG BUILD_VERSION 6 | ARG BUILD_COMMIT 7 | 8 | # required for generating the version descriptor 9 | RUN apk add --no-cache git 10 | 11 | WORKDIR /workspace 12 | 13 | # Copy the go source 14 | COPY . . 15 | 16 | # Build 17 | RUN --mount=type=cache,target=/go/pkg \ 18 | --mount=type=cache,target=/root/.cache/go-build \ 19 | GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags="-s -w -X github.com/overmindtech/cli/tracing.version=${BUILD_VERSION} -X github.com/overmindtech/cli/tracing.commit=${BUILD_COMMIT}" -o source aws-source/main.go 20 | 21 | FROM alpine:3.21 22 | WORKDIR / 23 | COPY --from=builder /workspace/source . 24 | USER 65534:65534 25 | 26 | ENTRYPOINT ["/source"] 27 | -------------------------------------------------------------------------------- /aws-source/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | nats: 4 | image: nats 5 | command: "-c /etc/nats/nats.conf -DV" #-c /etc/nats/nats.conf --cluster nats://0.0.0.0:6222 --routes=nats://ruser:T0pS3cr3t@nats:6222 6 | ports: 7 | - "4222:4222" 8 | - "8222:8222" 9 | - "6222:6222" 10 | - "4433:4433" 11 | volumes: 12 | - ./acceptance/nats-server.conf:/etc/nats/nats.conf 13 | # nats-1: 14 | # image: nats 15 | # command: "-c nats-server.conf --routes=nats-route://ruser:T0pS3cr3t@nats:6222 -DV" 16 | #link: 17 | # # Will build from a local copy 18 | # build: ../redacted_link 19 | # environment: 20 | # - REDACTED_NATS_URLS=nats 21 | # - REDACTED_VERBOSITY=debug 22 | 23 | networks: 24 | default: 25 | external: 26 | name: nats 27 | -------------------------------------------------------------------------------- /aws-source/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 {AUTHOR} 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "github.com/overmindtech/cli/aws-source/cmd" 20 | _ "go.uber.org/automaxprocs" 21 | ) 22 | 23 | func main() { 24 | cmd.Execute() 25 | } 26 | -------------------------------------------------------------------------------- /cmd/bookmarks.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2024 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | // bookmarksCmd represents the bookmarks command 11 | var bookmarksCmd = &cobra.Command{ 12 | Use: "bookmarks", 13 | GroupID: "api", 14 | Short: "Interact with the bookmarks that were created in the Explore view", 15 | Long: `A bookmark in Overmind is a set of queries that are stored together and can be 16 | executed as a single block.`, 17 | Run: func(cmd *cobra.Command, args []string) { 18 | _ = cmd.Help() 19 | }, 20 | } 21 | 22 | func init() { 23 | rootCmd.AddCommand(bookmarksCmd) 24 | 25 | addAPIFlags(bookmarksCmd) 26 | 27 | // Here you will define your flags and configuration settings. 28 | 29 | // Cobra supports Persistent Flags which will work for this command 30 | // and all subcommands, e.g.: 31 | // bookmarksCmd.PersistentFlags().String("foo", "", "A help for foo") 32 | 33 | // Cobra supports local flags which will only run when this command 34 | // is called directly, e.g.: 35 | // bookmarksCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 36 | } 37 | -------------------------------------------------------------------------------- /cmd/changes.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // changesCmd represents the changes command 8 | var changesCmd = &cobra.Command{ 9 | Use: "changes", 10 | GroupID: "api", 11 | Short: "Create, update and delete changes in Overmind", 12 | Long: `Manage changes that are being tracked using Overmind. NOTE: It is probably 13 | easier to use our IaC wrappers such as 'overmind terraform plan' rather than 14 | using these commands directly, but they are provided for flexibility.`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | _ = cmd.Help() 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(changesCmd) 22 | 23 | addAPIFlags(changesCmd) 24 | 25 | // Here you will define your flags and configuration settings. 26 | 27 | // Cobra supports Persistent Flags which will work for this command 28 | // and all subcommands, e.g.: 29 | // changesCmd.PersistentFlags().String("foo", "", "A help for foo") 30 | 31 | // Cobra supports local flags which will only run when this command 32 | // is called directly, e.g.: 33 | // changesCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 34 | } 35 | -------------------------------------------------------------------------------- /cmd/integrations.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // integrationsCmd represents the integrations command 8 | var integrationsCmd = &cobra.Command{ 9 | Use: "integrations", 10 | GroupID: "api", 11 | Short: "Manage integrations with Overmind", 12 | Long: `Manage integrations with Overmind. These integrations allow you to 13 | integrate Overmind with other tools and services.`, 14 | Run: func(cmd *cobra.Command, args []string) { 15 | _ = cmd.Help() 16 | }, 17 | } 18 | 19 | func init() { 20 | rootCmd.AddCommand(integrationsCmd) 21 | 22 | addAPIFlags(integrationsCmd) 23 | 24 | // Here you will define your flags and configuration settings. 25 | 26 | // Cobra supports Persistent Flags which will work for this command 27 | // and all subcommands, e.g.: 28 | // integrationsCmd.PersistentFlags().String("foo", "", "A help for foo") 29 | 30 | // Cobra supports local flags which will only run when this command 31 | // is called directly, e.g.: 32 | // integrationsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 33 | } 34 | -------------------------------------------------------------------------------- /cmd/invites.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // invitesCmd represents the invites command 8 | var invitesCmd = &cobra.Command{ 9 | Use: "invites", 10 | GroupID: "api", 11 | Short: "Manage invites for your team to Overmind", 12 | Long: `Create and revoke Overmind invitations`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | _ = cmd.Help() 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(invitesCmd) 20 | 21 | addAPIFlags(invitesCmd) 22 | // Here you will define your flags and configuration settings. 23 | 24 | // Cobra supports Persistent Flags which will work for this command 25 | // and all subcommands, e.g.: 26 | // invitesCmd.PersistentFlags().String("foo", "", "A help for foo") 27 | 28 | // Cobra supports local flags which will only run when this command 29 | // is called directly, e.g.: 30 | // invitesCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 31 | } 32 | -------------------------------------------------------------------------------- /cmd/logging.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/ttacon/chalk" 5 | ) 6 | 7 | var ( 8 | // Styles 9 | Underline = TextStyle{chalk.Underline} 10 | Bold = TextStyle{chalk.Bold} 11 | 12 | // Colors 13 | Black = Color{chalk.Black} 14 | Red = Color{chalk.Red} 15 | Green = Color{chalk.Green} 16 | Yellow = Color{chalk.Yellow} 17 | Blue = Color{chalk.Blue} 18 | Magenta = Color{chalk.Magenta} 19 | Cyan = Color{chalk.Cyan} 20 | White = Color{chalk.White} 21 | ) 22 | 23 | // A type that wraps chalk.TextStyle but adds detections for if we're in a TTY 24 | type TextStyle struct { 25 | underlying chalk.TextStyle 26 | } 27 | 28 | // A type that wraps chalk.Color but adds detections for if we're in a TTY 29 | type Color struct { 30 | underlying chalk.Color 31 | } 32 | -------------------------------------------------------------------------------- /cmd/snapshots.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // snapshotsCmd represents the snapshots command 8 | var snapshotsCmd = &cobra.Command{ 9 | Use: "snapshots", 10 | GroupID: "api", 11 | Short: "Create, view and delete snapshots if your infrastructure", 12 | Long: `Overmind automatically creates snapshots are part of the change lifecycle, 13 | however you can use these commands to interact directly with the API if 14 | required.`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | _ = cmd.Help() 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(snapshotsCmd) 22 | 23 | addAPIFlags(snapshotsCmd) 24 | 25 | // Here you will define your flags and configuration settings. 26 | 27 | // Cobra supports Persistent Flags which will work for this command 28 | // and all subcommands, e.g.: 29 | // snapshotsCmd.PersistentFlags().String("foo", "", "A help for foo") 30 | 31 | // Cobra supports local flags which will only run when this command 32 | // is called directly, e.g.: 33 | // snapshotsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 34 | } 35 | -------------------------------------------------------------------------------- /cmd/theme_darwin.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | // IsConhost returns true if the current terminal is conhost. This indicates 4 | // that it can't deal with multi-byte characters and requires special treatment. 5 | // See https://github.com/overmindtech/cli/issues/388 for detailed analysis. 6 | func IsConhost() bool { 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /cmd/theme_linux.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "sync" 7 | ) 8 | 9 | var isWslCache int // 0 = unset; 1 = WSL; 2 = not WSL 10 | var isWslCacheMu sync.RWMutex 11 | 12 | // IsConhost returns true if the current terminal is conhost. This indicates 13 | // that it can't deal with multi-byte characters and requires special treatment. 14 | // See https://github.com/overmindtech/cli/issues/388 for detailed analysis. 15 | func IsConhost() bool { 16 | // shortcut this if we (probably) run in Windows Terminal (through WSL) or 17 | // on something that smells like a regular Linux terminal 18 | if os.Getenv("WT_SESSION") != "" { 19 | return false 20 | } 21 | 22 | isWslCacheMu.RLock() 23 | w := isWslCache 24 | isWslCacheMu.RUnlock() 25 | 26 | switch w { 27 | case 1: 28 | return true 29 | case 2: 30 | return false 31 | } 32 | 33 | // isWslCache has not yet been initialised, so we need to check if we are in WSL 34 | // since we don't know if we are in WSL, we need to check now 35 | isWslCacheMu.Lock() 36 | defer isWslCacheMu.Unlock() 37 | if w != 0 { 38 | // someone else raced the lock and has already decided 39 | return isWslCache == 1 40 | } 41 | 42 | // check if we run in WSL 43 | ver, err := os.ReadFile("/proc/version") 44 | if err == nil && bytes.Contains(ver, []byte("Microsoft")) { 45 | isWslCache = 1 46 | return true 47 | } 48 | 49 | // we can't access /proc/version or it does not contain Microsoft, we are _probably_ not in WSL 50 | isWslCache = 2 51 | return false 52 | } 53 | -------------------------------------------------------------------------------- /cmd/theme_windows.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import "os" 4 | 5 | // IsConhost returns true if the current terminal is conhost. This indicates 6 | // that it can't deal with multi-byte characters and requires special treatment. 7 | // See https://github.com/overmindtech/cli/issues/388 for detailed analysis. 8 | func IsConhost() bool { 9 | return os.Getenv("WT_SESSION") == "" 10 | } 11 | -------------------------------------------------------------------------------- /demos/plan.tape: -------------------------------------------------------------------------------- 1 | Output demos/plan.gif 2 | # Output demos/plan.mp4 3 | 4 | Set Margin 20 5 | Set MarginFill "#7a70eb" # use Dark.BgMain 6 | Set BorderRadius 10 7 | 8 | Set Width 1200 9 | Set Height 900 10 | 11 | Set FontSize 15 12 | 13 | Hide 14 | Type "cd tmp" 15 | Enter 16 | Type "export PATH=$PWD:$PATH" 17 | Enter 18 | Type "clear" 19 | Enter 20 | Show 21 | 22 | Type@10ms "overmind terraform plan" 23 | Enter 24 | Sleep 2 25 | Down 26 | Sleep 500ms 27 | Down 28 | Sleep 1 29 | Enter 30 | Type "sso-dogfood" 31 | Sleep 1 32 | Enter 33 | Sleep 60 34 | Sleep 20 35 | 36 | # Ctrl+c 37 | # Sleep 10 38 | -------------------------------------------------------------------------------- /discovery/main_test.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/overmindtech/cli/tracing" 10 | ) 11 | 12 | func TestMain(m *testing.M) { 13 | exitCode := func() int { 14 | defer tracing.ShutdownTracer(context.Background()) 15 | 16 | if err := tracing.InitTracerWithUpstreams("discovery-tests", os.Getenv("HONEYCOMB_API_KEY"), ""); err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | return m.Run() 21 | }() 22 | 23 | os.Exit(exitCode) 24 | } 25 | -------------------------------------------------------------------------------- /discovery/nats_shared_test.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/url" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var NatsTestURLs = []string{ 12 | "nats://nats:4222", 13 | "nats://localhost:4222", 14 | } 15 | 16 | var NatsAuthTestURLs = []string{ 17 | "nats://nats-auth:4222", 18 | "nats://localhost:4223", 19 | } 20 | 21 | var tokenExchangeURLs = []string{ 22 | "http://api-server:8080/api", 23 | "http://localhost:8080/api", 24 | } 25 | 26 | // SkipWithoutNats Skips a test if NATS is not available 27 | func SkipWithoutNats(t *testing.T) { 28 | var err error 29 | 30 | for _, url := range NatsTestURLs { 31 | err = testURL(url) 32 | 33 | if err == nil { 34 | return 35 | } 36 | } 37 | 38 | if err != nil { 39 | t.Error(err) 40 | t.Skip("NATS not available") 41 | } 42 | } 43 | 44 | // SkipWithoutNatsAuth Skips a test if authenticated NATS is not available 45 | func SkipWithoutNatsAuth(t *testing.T) { 46 | var err error 47 | 48 | for _, url := range NatsAuthTestURLs { 49 | err = testURL(url) 50 | 51 | if err == nil { 52 | return 53 | } 54 | } 55 | 56 | if err != nil { 57 | t.Error(err) 58 | t.Skip("NATS not available") 59 | } 60 | } 61 | 62 | func GetWorkingTokenExchange() (string, error) { 63 | var err error 64 | 65 | for _, url := range tokenExchangeURLs { 66 | if err = testURL(url); err == nil { 67 | return url, nil 68 | } 69 | } 70 | 71 | return "", fmt.Errorf("no working token exchanges found: %w", err) 72 | } 73 | 74 | func testURL(testURL string) error { 75 | url, err := url.Parse(testURL) 76 | 77 | if err != nil { 78 | return fmt.Errorf("could not parse NATS URL: %v. Error: %w", testURL, err) 79 | } 80 | 81 | conn, err := net.DialTimeout("tcp", net.JoinHostPort(url.Hostname(), url.Port()), time.Second) 82 | 83 | if err == nil { 84 | conn.Close() 85 | return nil 86 | } 87 | 88 | return err 89 | } 90 | -------------------------------------------------------------------------------- /discovery/tracing.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "go.opentelemetry.io/otel" 5 | semconv "go.opentelemetry.io/otel/semconv/v1.26.0" 6 | "go.opentelemetry.io/otel/trace" 7 | ) 8 | 9 | const ( 10 | instrumentationName = "github.com/overmindtech/cli/discovery/discovery" 11 | instrumentationVersion = "0.0.1" 12 | ) 13 | 14 | var ( 15 | tracer = otel.GetTracerProvider().Tracer( 16 | instrumentationName, 17 | trace.WithInstrumentationVersion(instrumentationVersion), 18 | trace.WithSchemaURL(semconv.SchemaURL), 19 | ) 20 | ) 21 | -------------------------------------------------------------------------------- /examples/create-bookmark.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Changing items for 'CN=GTS Root R1,O=Google Trust Services'", 3 | "description": "This bookmark contains the items that are changing as part of the 'CN=GTS Root R1,O=Google Trust Services' change. Generated using UpdateChangingItems", 4 | "queries": [ 5 | { 6 | "type": "certificate", 7 | "query": "CN=GTS Root R1,O=Google Trust Services", 8 | "scope": "global" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /gon-amd64.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": ["./dist/overmind-macos_darwin_amd64_v1/overmind"], 3 | "bundle_id": "tech.overmind.cli", 4 | "apple_id": { 5 | "username": "dylanratcliffe@outlook.com", 6 | "provider": "248Q4U4VT7" 7 | }, 8 | "sign": { 9 | "application_identity": "CE61C10EED69BFB4B25EB349AD15B29D75A809B9" 10 | }, 11 | "dmg" :{ 12 | "output_path": "dist/overmind-cli-amd64.dmg", 13 | "volume_name": "Overmind" 14 | } 15 | } -------------------------------------------------------------------------------- /gon-arm64.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": ["./dist/overmind-macos_darwin_arm64/overmind"], 3 | "bundle_id": "tech.overmind.cli", 4 | "apple_id": { 5 | "username": "dylanratcliffe@outlook.com", 6 | "provider": "248Q4U4VT7" 7 | }, 8 | "sign": { 9 | "application_identity": "CE61C10EED69BFB4B25EB349AD15B29D75A809B9" 10 | }, 11 | "dmg" :{ 12 | "output_path": "dist/overmind-cli-arm64.dmg", 13 | "volume_name": "Overmind" 14 | } 15 | } -------------------------------------------------------------------------------- /k8s-source/acceptance/nats-server.conf: -------------------------------------------------------------------------------- 1 | # Client port of 4222 on all interfaces 2 | port: 4222 3 | 4 | # HTTP monitoring port 5 | monitor_port: 8222 6 | 7 | # This is for clustering multiple servers together. 8 | cluster { 9 | # It is recommended to set a cluster name 10 | name: "my_cluster" 11 | 12 | # Route connections to be received on any interface on port 6222 13 | port: 6222 14 | 15 | # Routes are protected, so need to use them with --routes flag 16 | # e.g. --routes=nats-route://ruser:T0pS3cr3t@otherdockerhost:6222 17 | authorization { 18 | user: ruser 19 | password: T0pS3cr3t 20 | timeout: 0.75 21 | } 22 | 23 | # Routes are actively solicited and connected to from this server. 24 | # This Docker image has none by default, but you can pass a 25 | # flag to the nats-server docker image to create one to an existing server. 26 | routes = [] 27 | } 28 | 29 | websocket { 30 | port: 4433 31 | no_tls: true 32 | } -------------------------------------------------------------------------------- /k8s-source/adapters/clusterrole.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/rbac/v1" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | func newClusterRoleAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 12 | return &KubeTypeAdapter[*v1.ClusterRole, *v1.ClusterRoleList]{ 13 | ClusterName: cluster, 14 | Namespaces: namespaces, 15 | TypeName: "ClusterRole", 16 | ClusterInterfaceBuilder: func() ItemInterface[*v1.ClusterRole, *v1.ClusterRoleList] { 17 | return cs.RbacV1().ClusterRoles() 18 | }, 19 | ListExtractor: func(list *v1.ClusterRoleList) ([]*v1.ClusterRole, error) { 20 | bindings := make([]*v1.ClusterRole, len(list.Items)) 21 | 22 | for i := range list.Items { 23 | bindings[i] = &list.Items[i] 24 | } 25 | 26 | return bindings, nil 27 | }, 28 | AdapterMetadata: clusterRoleAdapterMetadata, 29 | } 30 | } 31 | 32 | var clusterRoleAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 33 | Type: "ClusterRole", 34 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_SECURITY, 35 | DescriptiveName: "Cluster Role", 36 | SupportedQueryMethods: DefaultSupportedQueryMethods("Cluster Role"), 37 | TerraformMappings: []*sdp.TerraformMapping{ 38 | { 39 | TerraformMethod: sdp.QueryMethod_GET, 40 | TerraformQueryMap: "kubernetes_cluster_role_v1.metadata[0].name", 41 | }, 42 | }, 43 | }) 44 | 45 | func init() { 46 | registerAdapterLoader(newClusterRoleAdapter) 47 | } 48 | -------------------------------------------------------------------------------- /k8s-source/adapters/clusterrole_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var clusterRoleYAML = ` 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRole 10 | metadata: 11 | name: read-only 12 | rules: 13 | - apiGroups: [""] 14 | resources: ["*"] 15 | verbs: ["get", "list", "watch"] 16 | 17 | ` 18 | 19 | func TestClusterRoleAdapter(t *testing.T) { 20 | adapter := newClusterRoleAdapter(CurrentCluster.ClientSet, CurrentCluster.Name, []string{}) 21 | 22 | st := AdapterTests{ 23 | Adapter: adapter, 24 | GetQuery: "read-only", 25 | GetScope: CurrentCluster.Name, 26 | SetupYAML: clusterRoleYAML, 27 | GetQueryTests: QueryTests{}, 28 | } 29 | 30 | st.Execute(t) 31 | } 32 | -------------------------------------------------------------------------------- /k8s-source/adapters/clusterrolebinding_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | ) 8 | 9 | var clusterRoleBindingYAML = ` 10 | apiVersion: rbac.authorization.k8s.io/v1 11 | kind: ClusterRoleBinding 12 | metadata: 13 | name: admin-binding 14 | subjects: 15 | - kind: Group 16 | name: system:serviceaccounts:default 17 | apiGroup: rbac.authorization.k8s.io 18 | roleRef: 19 | kind: ClusterRole 20 | name: admin 21 | apiGroup: rbac.authorization.k8s.io 22 | ` 23 | 24 | func TestClusterRoleBindingAdapter(t *testing.T) { 25 | adapter := newClusterRoleBindingAdapter(CurrentCluster.ClientSet, CurrentCluster.Name, []string{}) 26 | 27 | st := AdapterTests{ 28 | Adapter: adapter, 29 | GetQuery: "admin-binding", 30 | GetScope: CurrentCluster.Name, 31 | SetupYAML: clusterRoleBindingYAML, 32 | GetQueryTests: QueryTests{ 33 | { 34 | ExpectedType: "ClusterRole", 35 | ExpectedMethod: sdp.QueryMethod_GET, 36 | ExpectedQuery: "admin", 37 | ExpectedScope: CurrentCluster.Name, 38 | }, 39 | { 40 | ExpectedType: "Group", 41 | ExpectedMethod: sdp.QueryMethod_GET, 42 | ExpectedQuery: "system:serviceaccounts:default", 43 | ExpectedScope: CurrentCluster.Name, 44 | }, 45 | }, 46 | } 47 | 48 | st.Execute(t) 49 | } 50 | -------------------------------------------------------------------------------- /k8s-source/adapters/configmap.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | func newConfigMapAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 11 | return &KubeTypeAdapter[*v1.ConfigMap, *v1.ConfigMapList]{ 12 | ClusterName: cluster, 13 | Namespaces: namespaces, 14 | TypeName: "ConfigMap", 15 | AutoQueryExtract: true, 16 | NamespacedInterfaceBuilder: func(namespace string) ItemInterface[*v1.ConfigMap, *v1.ConfigMapList] { 17 | return cs.CoreV1().ConfigMaps(namespace) 18 | }, 19 | ListExtractor: func(list *v1.ConfigMapList) ([]*v1.ConfigMap, error) { 20 | bindings := make([]*v1.ConfigMap, len(list.Items)) 21 | 22 | for i := range list.Items { 23 | bindings[i] = &list.Items[i] 24 | } 25 | 26 | return bindings, nil 27 | }, 28 | AdapterMetadata: configMapAdapterMetadata, 29 | } 30 | } 31 | 32 | var configMapAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 33 | Type: "ConfigMap", 34 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_CONFIGURATION, 35 | DescriptiveName: "Config Map", 36 | SupportedQueryMethods: DefaultSupportedQueryMethods("Config Map"), 37 | TerraformMappings: []*sdp.TerraformMapping{ 38 | { 39 | TerraformMethod: sdp.QueryMethod_GET, 40 | TerraformQueryMap: "kubernetes_config_map_v1.metadata[0].name", 41 | }, 42 | { 43 | TerraformMethod: sdp.QueryMethod_GET, 44 | TerraformQueryMap: "kubernetes_config_map.metadata[0].name", 45 | }, 46 | }, 47 | }) 48 | 49 | func init() { 50 | registerAdapterLoader(newConfigMapAdapter) 51 | } 52 | -------------------------------------------------------------------------------- /k8s-source/adapters/configmap_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import "testing" 4 | 5 | var configMapYAML = ` 6 | apiVersion: v1 7 | kind: ConfigMap 8 | metadata: 9 | name: my-configmap 10 | data: 11 | DATABASE_URL: "postgres://myuser:mypassword@mydbhost:5432/mydatabase" 12 | APP_SECRET_KEY: "mysecretkey123" 13 | ` 14 | 15 | func TestConfigMapAdapter(t *testing.T) { 16 | sd := ScopeDetails{ 17 | ClusterName: CurrentCluster.Name, 18 | Namespace: "default", 19 | } 20 | 21 | adapter := newConfigMapAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 22 | 23 | st := AdapterTests{ 24 | Adapter: adapter, 25 | GetQuery: "my-configmap", 26 | GetScope: sd.String(), 27 | SetupYAML: configMapYAML, 28 | GetQueryTests: QueryTests{}, 29 | } 30 | 31 | st.Execute(t) 32 | } 33 | -------------------------------------------------------------------------------- /k8s-source/adapters/cronjob.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/batch/v1" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | func newCronJobAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 12 | return &KubeTypeAdapter[*v1.CronJob, *v1.CronJobList]{ 13 | ClusterName: cluster, 14 | Namespaces: namespaces, 15 | TypeName: "CronJob", 16 | AutoQueryExtract: true, 17 | NamespacedInterfaceBuilder: func(namespace string) ItemInterface[*v1.CronJob, *v1.CronJobList] { 18 | return cs.BatchV1().CronJobs(namespace) 19 | }, 20 | ListExtractor: func(list *v1.CronJobList) ([]*v1.CronJob, error) { 21 | bindings := make([]*v1.CronJob, len(list.Items)) 22 | 23 | for i := range list.Items { 24 | bindings[i] = &list.Items[i] 25 | } 26 | 27 | return bindings, nil 28 | }, 29 | // Cronjobs don't need linked items as the jobs they produce are linked 30 | // automatically 31 | AdapterMetadata: cronJobAdapterMetadata, 32 | } 33 | } 34 | 35 | var cronJobAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 36 | Type: "CronJob", 37 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_COMPUTE_APPLICATION, 38 | DescriptiveName: "Cron Job", 39 | SupportedQueryMethods: DefaultSupportedQueryMethods("Cron Job"), 40 | TerraformMappings: []*sdp.TerraformMapping{ 41 | { 42 | TerraformMethod: sdp.QueryMethod_GET, 43 | TerraformQueryMap: "kubernetes_cron_job_v1.metadata[0].name", 44 | }, 45 | { 46 | TerraformMethod: sdp.QueryMethod_GET, 47 | TerraformQueryMap: "kubernetes_cron_job.metadata[0].name", 48 | }, 49 | }, 50 | }) 51 | 52 | func init() { 53 | registerAdapterLoader(newCronJobAdapter) 54 | } 55 | -------------------------------------------------------------------------------- /k8s-source/adapters/daemonset.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/apps/v1" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | func newDaemonSetAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 12 | return &KubeTypeAdapter[*v1.DaemonSet, *v1.DaemonSetList]{ 13 | ClusterName: cluster, 14 | Namespaces: namespaces, 15 | TypeName: "DaemonSet", 16 | AutoQueryExtract: true, 17 | NamespacedInterfaceBuilder: func(namespace string) ItemInterface[*v1.DaemonSet, *v1.DaemonSetList] { 18 | return cs.AppsV1().DaemonSets(namespace) 19 | }, 20 | ListExtractor: func(list *v1.DaemonSetList) ([]*v1.DaemonSet, error) { 21 | extracted := make([]*v1.DaemonSet, len(list.Items)) 22 | 23 | for i := range list.Items { 24 | extracted[i] = &list.Items[i] 25 | } 26 | 27 | return extracted, nil 28 | }, 29 | // Pods are linked automatically 30 | AdapterMetadata: daemonSetAdapterMetadata, 31 | } 32 | } 33 | 34 | var daemonSetAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 35 | Type: "DaemonSet", 36 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_COMPUTE_APPLICATION, 37 | DescriptiveName: "Daemon Set", 38 | SupportedQueryMethods: DefaultSupportedQueryMethods("Daemon Set"), 39 | TerraformMappings: []*sdp.TerraformMapping{ 40 | { 41 | TerraformMethod: sdp.QueryMethod_GET, 42 | TerraformQueryMap: "kubernetes_daemon_set_v1.metadata[0].name", 43 | }, 44 | { 45 | TerraformMethod: sdp.QueryMethod_GET, 46 | TerraformQueryMap: "kubernetes_daemonset.metadata[0].name", 47 | }, 48 | }, 49 | }) 50 | 51 | func init() { 52 | registerAdapterLoader(newDaemonSetAdapter) 53 | } 54 | -------------------------------------------------------------------------------- /k8s-source/adapters/daemonset_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var daemonSetYAML = ` 8 | apiVersion: apps/v1 9 | kind: DaemonSet 10 | metadata: 11 | name: my-daemonset 12 | spec: 13 | selector: 14 | matchLabels: 15 | app: my-app 16 | template: 17 | metadata: 18 | labels: 19 | app: my-app 20 | spec: 21 | containers: 22 | - name: my-container 23 | image: nginx:latest 24 | ports: 25 | - containerPort: 80 26 | 27 | ` 28 | 29 | func TestDaemonSetSource(t *testing.T) { 30 | sd := ScopeDetails{ 31 | ClusterName: CurrentCluster.Name, 32 | Namespace: "default", 33 | } 34 | 35 | adapter := newDaemonSetAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 36 | 37 | st := AdapterTests{ 38 | Adapter: adapter, 39 | GetQuery: "my-daemonset", 40 | GetScope: sd.String(), 41 | SetupYAML: daemonSetYAML, 42 | } 43 | 44 | st.Execute(t) 45 | } 46 | -------------------------------------------------------------------------------- /k8s-source/adapters/deployment_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | var deploymentYAML = ` 11 | apiVersion: apps/v1 12 | kind: Deployment 13 | metadata: 14 | name: my-deployment 15 | spec: 16 | replicas: 1 17 | selector: 18 | matchLabels: 19 | app: my-deployment 20 | template: 21 | metadata: 22 | labels: 23 | app: my-deployment 24 | spec: 25 | containers: 26 | - name: my-container 27 | image: nginx:latest 28 | ports: 29 | - containerPort: 80 30 | ` 31 | 32 | func TestDeploymentSource(t *testing.T) { 33 | sd := ScopeDetails{ 34 | ClusterName: CurrentCluster.Name, 35 | Namespace: "default", 36 | } 37 | 38 | adapter := newDeploymentAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 39 | 40 | st := AdapterTests{ 41 | Adapter: adapter, 42 | GetQuery: "my-deployment", 43 | GetScope: sd.String(), 44 | SetupYAML: deploymentYAML, 45 | Wait: func(item *sdp.Item) bool { 46 | return item.GetHealth() == sdp.Health_HEALTH_OK 47 | }, 48 | GetQueryTests: QueryTests{ 49 | { 50 | ExpectedType: "ReplicaSet", 51 | ExpectedMethod: sdp.QueryMethod_GET, 52 | ExpectedScope: "local-tests.default", 53 | ExpectedQueryMatches: regexp.MustCompile("my-deployment"), 54 | }, 55 | }, 56 | } 57 | 58 | st.Execute(t) 59 | } 60 | -------------------------------------------------------------------------------- /k8s-source/adapters/horizontalpodautoscaler_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | ) 8 | 9 | var horizontalPodAutoscalerYAML = ` 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | metadata: 13 | name: hpa-deployment 14 | spec: 15 | replicas: 1 16 | selector: 17 | matchLabels: 18 | app: hpa-app 19 | template: 20 | metadata: 21 | labels: 22 | app: hpa-app 23 | spec: 24 | containers: 25 | - name: hpa-container 26 | image: nginx:latest 27 | ports: 28 | - containerPort: 80 29 | --- 30 | apiVersion: autoscaling/v2 31 | kind: HorizontalPodAutoscaler 32 | metadata: 33 | name: my-hpa 34 | spec: 35 | scaleTargetRef: 36 | apiVersion: apps/v1 37 | kind: Deployment 38 | name: hpa-deployment 39 | minReplicas: 1 40 | maxReplicas: 10 41 | metrics: 42 | - type: Resource 43 | resource: 44 | name: cpu 45 | target: 46 | type: Utilization 47 | averageUtilization: 50 48 | ` 49 | 50 | func TestHorizontalPodAutoscalerAdapter(t *testing.T) { 51 | sd := ScopeDetails{ 52 | ClusterName: CurrentCluster.Name, 53 | Namespace: "default", 54 | } 55 | 56 | adapter := newHorizontalPodAutoscalerAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 57 | 58 | st := AdapterTests{ 59 | Adapter: adapter, 60 | GetQuery: "my-hpa", 61 | GetScope: sd.String(), 62 | SetupYAML: horizontalPodAutoscalerYAML, 63 | GetQueryTests: QueryTests{ 64 | { 65 | ExpectedType: "Deployment", 66 | ExpectedMethod: sdp.QueryMethod_GET, 67 | ExpectedScope: sd.String(), 68 | ExpectedQuery: "hpa-deployment", 69 | }, 70 | }, 71 | } 72 | 73 | st.Execute(t) 74 | } 75 | -------------------------------------------------------------------------------- /k8s-source/adapters/job_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | var jobYAML = ` 11 | apiVersion: batch/v1 12 | kind: Job 13 | metadata: 14 | name: my-job 15 | spec: 16 | template: 17 | spec: 18 | containers: 19 | - name: my-container 20 | image: nginx 21 | command: ["/bin/sh", "-c"] 22 | args: 23 | - echo "Hello, world!"; sleep 5 24 | restartPolicy: OnFailure 25 | backoffLimit: 4 26 | --- 27 | apiVersion: batch/v1 28 | kind: Job 29 | metadata: 30 | name: my-job2 31 | spec: 32 | template: 33 | spec: 34 | containers: 35 | - name: my-container 36 | image: nginx 37 | command: ["/bin/sh", "-c"] 38 | args: 39 | - echo "Hello, world!"; sleep 5 40 | restartPolicy: OnFailure 41 | backoffLimit: 4 42 | ` 43 | 44 | func TestJobAdapter(t *testing.T) { 45 | sd := ScopeDetails{ 46 | ClusterName: CurrentCluster.Name, 47 | Namespace: "default", 48 | } 49 | 50 | adapter := newJobAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 51 | 52 | st := AdapterTests{ 53 | Adapter: adapter, 54 | GetQuery: "my-job", 55 | GetScope: sd.String(), 56 | SetupYAML: jobYAML, 57 | GetQueryTests: QueryTests{ 58 | { 59 | ExpectedQueryMatches: regexp.MustCompile("controller-uid="), 60 | ExpectedType: "Pod", 61 | ExpectedMethod: sdp.QueryMethod_SEARCH, 62 | ExpectedScope: sd.String(), 63 | }, 64 | }, 65 | } 66 | 67 | st.Execute(t) 68 | } 69 | -------------------------------------------------------------------------------- /k8s-source/adapters/limitrange.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | func newLimitRangeAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 11 | return &KubeTypeAdapter[*v1.LimitRange, *v1.LimitRangeList]{ 12 | ClusterName: cluster, 13 | Namespaces: namespaces, 14 | TypeName: "LimitRange", 15 | NamespacedInterfaceBuilder: func(namespace string) ItemInterface[*v1.LimitRange, *v1.LimitRangeList] { 16 | return cs.CoreV1().LimitRanges(namespace) 17 | }, 18 | ListExtractor: func(list *v1.LimitRangeList) ([]*v1.LimitRange, error) { 19 | extracted := make([]*v1.LimitRange, len(list.Items)) 20 | 21 | for i := range list.Items { 22 | extracted[i] = &list.Items[i] 23 | } 24 | 25 | return extracted, nil 26 | }, 27 | AdapterMetadata: limitRangeAdapterMetadata, 28 | } 29 | } 30 | 31 | var limitRangeAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 32 | Type: "LimitRange", 33 | DescriptiveName: "Limit Range", 34 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_CONFIGURATION, 35 | SupportedQueryMethods: DefaultSupportedQueryMethods("Limit Range"), 36 | TerraformMappings: []*sdp.TerraformMapping{ 37 | { 38 | TerraformMethod: sdp.QueryMethod_GET, 39 | TerraformQueryMap: "kubernetes_limit_range_v1.metadata[0].name", 40 | }, 41 | { 42 | TerraformMethod: sdp.QueryMethod_GET, 43 | TerraformQueryMap: "kubernetes_limit_range.metadata[0].name", 44 | }, 45 | }, 46 | }) 47 | 48 | func init() { 49 | registerAdapterLoader(newLimitRangeAdapter) 50 | } 51 | -------------------------------------------------------------------------------- /k8s-source/adapters/limitrange_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var limitRangeYAML = ` 8 | apiVersion: v1 9 | kind: LimitRange 10 | metadata: 11 | name: example-limit-range 12 | spec: 13 | limits: 14 | - type: Pod 15 | max: 16 | memory: 200Mi 17 | min: 18 | cpu: 50m 19 | - type: Container 20 | max: 21 | memory: 150Mi 22 | cpu: 100m 23 | min: 24 | memory: 50Mi 25 | cpu: 50m 26 | default: 27 | memory: 100Mi 28 | cpu: 50m 29 | defaultRequest: 30 | memory: 80Mi 31 | cpu: 50m 32 | ` 33 | 34 | func TestLimitRangeAdapter(t *testing.T) { 35 | sd := ScopeDetails{ 36 | ClusterName: CurrentCluster.Name, 37 | Namespace: "default", 38 | } 39 | 40 | adapter := newLimitRangeAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 41 | 42 | st := AdapterTests{ 43 | Adapter: adapter, 44 | GetQuery: "example-limit-range", 45 | GetScope: sd.String(), 46 | SetupYAML: limitRangeYAML, 47 | GetQueryTests: QueryTests{}, 48 | } 49 | 50 | st.Execute(t) 51 | } 52 | -------------------------------------------------------------------------------- /k8s-source/adapters/main.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "k8s.io/client-go/kubernetes" 6 | ) 7 | 8 | type AdapterLoader func(clientSet *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter 9 | 10 | var adapterLoaders []AdapterLoader 11 | 12 | func registerAdapterLoader(loader AdapterLoader) { 13 | adapterLoaders = append(adapterLoaders, loader) 14 | } 15 | 16 | func LoadAllAdapters(cs *kubernetes.Clientset, cluster string, namespaces []string) []discovery.Adapter { 17 | adapters := make([]discovery.Adapter, len(adapterLoaders)) 18 | 19 | for i, loader := range adapterLoaders { 20 | adapters[i] = loader(cs, cluster, namespaces) 21 | } 22 | 23 | return adapters 24 | } 25 | -------------------------------------------------------------------------------- /k8s-source/adapters/networkpolicy_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | var NetworkPolicyYAML = ` 11 | apiVersion: networking.k8s.io/v1 12 | kind: NetworkPolicy 13 | metadata: 14 | name: allow-nginx 15 | spec: 16 | podSelector: 17 | matchLabels: 18 | app: nginx 19 | policyTypes: 20 | - Ingress 21 | ingress: 22 | - from: 23 | - podSelector: 24 | matchLabels: 25 | app: frontend 26 | ports: 27 | - protocol: TCP 28 | port: 80 29 | ` 30 | 31 | func TestNetworkPolicyAdapter(t *testing.T) { 32 | sd := ScopeDetails{ 33 | ClusterName: CurrentCluster.Name, 34 | Namespace: "default", 35 | } 36 | 37 | adapter := newNetworkPolicyAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 38 | 39 | st := AdapterTests{ 40 | Adapter: adapter, 41 | GetQuery: "allow-nginx", 42 | GetScope: sd.String(), 43 | SetupYAML: NetworkPolicyYAML, 44 | GetQueryTests: QueryTests{ 45 | { 46 | ExpectedQueryMatches: regexp.MustCompile("nginx"), 47 | ExpectedType: "Pod", 48 | ExpectedMethod: sdp.QueryMethod_SEARCH, 49 | ExpectedScope: sd.String(), 50 | }, 51 | }, 52 | } 53 | 54 | st.Execute(t) 55 | } 56 | -------------------------------------------------------------------------------- /k8s-source/adapters/node_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | func TestNodeAdapter(t *testing.T) { 11 | sd := ScopeDetails{ 12 | ClusterName: CurrentCluster.Name, 13 | Namespace: "default", 14 | } 15 | 16 | adapter := newNodeAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 17 | 18 | st := AdapterTests{ 19 | Adapter: adapter, 20 | GetQuery: "local-tests-control-plane", 21 | GetScope: sd.String(), 22 | GetQueryTests: QueryTests{ 23 | { 24 | ExpectedType: "ip", 25 | ExpectedMethod: sdp.QueryMethod_GET, 26 | ExpectedScope: "global", 27 | ExpectedQueryMatches: regexp.MustCompile(`172\.`), 28 | }, 29 | }, 30 | } 31 | 32 | st.Execute(t) 33 | } 34 | -------------------------------------------------------------------------------- /k8s-source/adapters/persistentvolume_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var persistentVolumeYAML = ` 8 | --- 9 | apiVersion: v1 10 | kind: PersistentVolume 11 | metadata: 12 | name: pv-test-pv 13 | spec: 14 | capacity: 15 | storage: 1Gi 16 | accessModes: 17 | - ReadWriteOnce 18 | hostPath: 19 | path: /tmp/pv-test-pv 20 | ` 21 | 22 | func TestPersistentVolumeAdapter(t *testing.T) { 23 | sd := ScopeDetails{ 24 | ClusterName: CurrentCluster.Name, 25 | Namespace: "", 26 | } 27 | 28 | adapter := newPersistentVolumeAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 29 | 30 | st := AdapterTests{ 31 | Adapter: adapter, 32 | GetQuery: "pv-test-pv", 33 | GetScope: sd.String(), 34 | SetupYAML: persistentVolumeYAML, 35 | GetQueryTests: QueryTests{}, 36 | } 37 | 38 | st.Execute(t) 39 | } 40 | -------------------------------------------------------------------------------- /k8s-source/adapters/poddisruptionbudget_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | var PodDisruptionBudgetYAML = ` 11 | apiVersion: policy/v1 12 | kind: PodDisruptionBudget 13 | metadata: 14 | name: example-pdb 15 | spec: 16 | minAvailable: 2 17 | selector: 18 | matchLabels: 19 | app: example-app 20 | ` 21 | 22 | func TestPodDisruptionBudgetAdapter(t *testing.T) { 23 | sd := ScopeDetails{ 24 | ClusterName: CurrentCluster.Name, 25 | Namespace: "default", 26 | } 27 | 28 | adapter := newPodDisruptionBudgetAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 29 | 30 | st := AdapterTests{ 31 | Adapter: adapter, 32 | GetQuery: "example-pdb", 33 | GetScope: sd.String(), 34 | SetupYAML: PodDisruptionBudgetYAML, 35 | GetQueryTests: QueryTests{ 36 | { 37 | ExpectedQueryMatches: regexp.MustCompile("app=example-app"), 38 | ExpectedType: "Pod", 39 | ExpectedMethod: sdp.QueryMethod_SEARCH, 40 | ExpectedScope: sd.String(), 41 | }, 42 | }, 43 | } 44 | 45 | st.Execute(t) 46 | } 47 | -------------------------------------------------------------------------------- /k8s-source/adapters/priorityclass.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/scheduling/v1" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | func newPriorityClassAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 12 | return &KubeTypeAdapter[*v1.PriorityClass, *v1.PriorityClassList]{ 13 | ClusterName: cluster, 14 | Namespaces: namespaces, 15 | TypeName: "PriorityClass", 16 | ClusterInterfaceBuilder: func() ItemInterface[*v1.PriorityClass, *v1.PriorityClassList] { 17 | return cs.SchedulingV1().PriorityClasses() 18 | }, 19 | ListExtractor: func(list *v1.PriorityClassList) ([]*v1.PriorityClass, error) { 20 | extracted := make([]*v1.PriorityClass, len(list.Items)) 21 | 22 | for i := range list.Items { 23 | extracted[i] = &list.Items[i] 24 | } 25 | 26 | return extracted, nil 27 | }, 28 | AdapterMetadata: priorityClassAdapterMetadata, 29 | } 30 | } 31 | 32 | var priorityClassAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 33 | Type: "PriorityClass", 34 | DescriptiveName: "Priority Class", 35 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_CONFIGURATION, 36 | SupportedQueryMethods: DefaultSupportedQueryMethods("Priority Class"), 37 | TerraformMappings: []*sdp.TerraformMapping{ 38 | { 39 | TerraformMethod: sdp.QueryMethod_GET, 40 | TerraformQueryMap: "kubernetes_priority_class_v1.metadata[0].name", 41 | }, 42 | { 43 | TerraformMethod: sdp.QueryMethod_GET, 44 | TerraformQueryMap: "kubernetes_priority_class.metadata[0].name", 45 | }, 46 | }, 47 | }) 48 | 49 | func init() { 50 | registerAdapterLoader(newPriorityClassAdapter) 51 | } 52 | -------------------------------------------------------------------------------- /k8s-source/adapters/priorityclass_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var priorityClassYAML = ` 8 | apiVersion: scheduling.k8s.io/v1 9 | kind: PriorityClass 10 | metadata: 11 | name: ultra-mega-priority 12 | value: 1000000 13 | globalDefault: false 14 | description: "This priority class should be used for ultra-mega-priority workloads" 15 | ` 16 | 17 | func TestPriorityClassAdapter(t *testing.T) { 18 | sd := ScopeDetails{ 19 | ClusterName: CurrentCluster.Name, 20 | Namespace: "default", 21 | } 22 | 23 | adapter := newPriorityClassAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 24 | 25 | st := AdapterTests{ 26 | Adapter: adapter, 27 | GetQuery: "ultra-mega-priority", 28 | GetScope: sd.String(), 29 | SetupYAML: priorityClassYAML, 30 | GetQueryTests: QueryTests{}, 31 | } 32 | 33 | st.Execute(t) 34 | } 35 | -------------------------------------------------------------------------------- /k8s-source/adapters/replicaset_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | var replicaSetYAML = ` 11 | apiVersion: apps/v1 12 | kind: ReplicaSet 13 | metadata: 14 | name: replica-set-test 15 | spec: 16 | replicas: 1 17 | selector: 18 | matchLabels: 19 | app: replica-set-test 20 | template: 21 | metadata: 22 | labels: 23 | app: replica-set-test 24 | spec: 25 | containers: 26 | - name: replica-set-test 27 | image: nginx:latest 28 | ports: 29 | - containerPort: 80 30 | 31 | ` 32 | 33 | func TestReplicaSetAdapter(t *testing.T) { 34 | sd := ScopeDetails{ 35 | ClusterName: CurrentCluster.Name, 36 | Namespace: "default", 37 | } 38 | 39 | adapter := newReplicaSetAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 40 | 41 | st := AdapterTests{ 42 | Adapter: adapter, 43 | GetQuery: "replica-set-test", 44 | GetScope: sd.String(), 45 | SetupYAML: replicaSetYAML, 46 | GetQueryTests: QueryTests{ 47 | { 48 | ExpectedQueryMatches: regexp.MustCompile("app=replica-set-test"), 49 | ExpectedType: "Pod", 50 | ExpectedMethod: sdp.QueryMethod_SEARCH, 51 | ExpectedScope: sd.String(), 52 | }, 53 | }, 54 | } 55 | 56 | st.Execute(t) 57 | } 58 | -------------------------------------------------------------------------------- /k8s-source/adapters/replicationcontroller_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | var replicationControllerYAML = ` 11 | apiVersion: v1 12 | kind: ReplicationController 13 | metadata: 14 | name: replication-controller-test 15 | spec: 16 | replicas: 1 17 | selector: 18 | app: replication-controller-test 19 | template: 20 | metadata: 21 | labels: 22 | app: replication-controller-test 23 | spec: 24 | containers: 25 | - name: nginx 26 | image: nginx:latest 27 | ports: 28 | - containerPort: 80 29 | 30 | ` 31 | 32 | func TestReplicationControllerAdapter(t *testing.T) { 33 | sd := ScopeDetails{ 34 | ClusterName: CurrentCluster.Name, 35 | Namespace: "default", 36 | } 37 | 38 | adapter := newReplicationControllerAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 39 | 40 | st := AdapterTests{ 41 | Adapter: adapter, 42 | GetQuery: "replication-controller-test", 43 | GetScope: sd.String(), 44 | SetupYAML: replicationControllerYAML, 45 | GetQueryTests: QueryTests{ 46 | { 47 | ExpectedQueryMatches: regexp.MustCompile("app=replication-controller-test"), 48 | ExpectedType: "Pod", 49 | ExpectedMethod: sdp.QueryMethod_SEARCH, 50 | ExpectedScope: sd.String(), 51 | }, 52 | }, 53 | } 54 | 55 | st.Execute(t) 56 | } 57 | -------------------------------------------------------------------------------- /k8s-source/adapters/resourcequota.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | func newResourceQuotaAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 11 | return &KubeTypeAdapter[*v1.ResourceQuota, *v1.ResourceQuotaList]{ 12 | ClusterName: cluster, 13 | Namespaces: namespaces, 14 | TypeName: "ResourceQuota", 15 | NamespacedInterfaceBuilder: func(namespace string) ItemInterface[*v1.ResourceQuota, *v1.ResourceQuotaList] { 16 | return cs.CoreV1().ResourceQuotas(namespace) 17 | }, 18 | ListExtractor: func(list *v1.ResourceQuotaList) ([]*v1.ResourceQuota, error) { 19 | extracted := make([]*v1.ResourceQuota, len(list.Items)) 20 | 21 | for i := range list.Items { 22 | extracted[i] = &list.Items[i] 23 | } 24 | 25 | return extracted, nil 26 | }, 27 | AdapterMetadata: resourceQuotaAdapterMetadata, 28 | } 29 | } 30 | 31 | var resourceQuotaAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 32 | Type: "ResourceQuota", 33 | DescriptiveName: "Resource Quota", 34 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_CONFIGURATION, 35 | SupportedQueryMethods: DefaultSupportedQueryMethods("Resource Quota"), 36 | TerraformMappings: []*sdp.TerraformMapping{ 37 | { 38 | TerraformMethod: sdp.QueryMethod_GET, 39 | TerraformQueryMap: "kubernetes_resource_quota_v1.metadata[0].name", 40 | }, 41 | { 42 | TerraformMethod: sdp.QueryMethod_GET, 43 | TerraformQueryMap: "kubernetes_resource_quota.metadata[0].name", 44 | }, 45 | }, 46 | }) 47 | 48 | func init() { 49 | registerAdapterLoader(newResourceQuotaAdapter) 50 | } 51 | -------------------------------------------------------------------------------- /k8s-source/adapters/resourcequota_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var resourceQuotaYAML = ` 8 | apiVersion: v1 9 | kind: ResourceQuota 10 | metadata: 11 | name: quota-example 12 | spec: 13 | hard: 14 | pods: "10" 15 | requests.cpu: "2" 16 | requests.memory: 2Gi 17 | limits.cpu: "4" 18 | limits.memory: 4Gi 19 | ` 20 | 21 | func TestResourceQuotaAdapter(t *testing.T) { 22 | sd := ScopeDetails{ 23 | ClusterName: CurrentCluster.Name, 24 | Namespace: "default", 25 | } 26 | 27 | adapter := newResourceQuotaAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 28 | 29 | st := AdapterTests{ 30 | Adapter: adapter, 31 | GetQuery: "quota-example", 32 | GetScope: sd.String(), 33 | SetupYAML: resourceQuotaYAML, 34 | } 35 | 36 | st.Execute(t) 37 | } 38 | -------------------------------------------------------------------------------- /k8s-source/adapters/role.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/rbac/v1" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | func newRoleAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 12 | return &KubeTypeAdapter[*v1.Role, *v1.RoleList]{ 13 | ClusterName: cluster, 14 | Namespaces: namespaces, 15 | TypeName: "Role", 16 | NamespacedInterfaceBuilder: func(namespace string) ItemInterface[*v1.Role, *v1.RoleList] { 17 | return cs.RbacV1().Roles(namespace) 18 | }, 19 | ListExtractor: func(list *v1.RoleList) ([]*v1.Role, error) { 20 | extracted := make([]*v1.Role, len(list.Items)) 21 | 22 | for i := range list.Items { 23 | extracted[i] = &list.Items[i] 24 | } 25 | 26 | return extracted, nil 27 | }, 28 | AdapterMetadata: roleAdapterMetadata, 29 | } 30 | } 31 | 32 | var roleAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 33 | Type: "Role", 34 | DescriptiveName: "Role", 35 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_SECURITY, 36 | SupportedQueryMethods: DefaultSupportedQueryMethods("Role"), 37 | TerraformMappings: []*sdp.TerraformMapping{ 38 | { 39 | TerraformMethod: sdp.QueryMethod_GET, 40 | TerraformQueryMap: "kubernetes_role_v1.metadata[0].name", 41 | }, 42 | { 43 | TerraformMethod: sdp.QueryMethod_GET, 44 | TerraformQueryMap: "kubernetes_role.metadata[0].name", 45 | }, 46 | }, 47 | }) 48 | 49 | func init() { 50 | registerAdapterLoader(newRoleAdapter) 51 | } 52 | -------------------------------------------------------------------------------- /k8s-source/adapters/role_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var RoleYAML = ` 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: Role 10 | metadata: 11 | name: role-test-role 12 | rules: 13 | - apiGroups: 14 | - "" 15 | - "apps" 16 | - "batch" 17 | - "extensions" 18 | resources: 19 | - pods 20 | - deployments 21 | - jobs 22 | - cronjobs 23 | - configmaps 24 | - secrets 25 | verbs: 26 | - get 27 | - list 28 | - watch 29 | - create 30 | - update 31 | - delete 32 | ` 33 | 34 | func TestRoleAdapter(t *testing.T) { 35 | sd := ScopeDetails{ 36 | ClusterName: CurrentCluster.Name, 37 | Namespace: "default", 38 | } 39 | 40 | adapter := newRoleAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 41 | 42 | st := AdapterTests{ 43 | Adapter: adapter, 44 | GetQuery: "role-test-role", 45 | GetScope: sd.String(), 46 | SetupYAML: RoleYAML, 47 | } 48 | 49 | st.Execute(t) 50 | } 51 | -------------------------------------------------------------------------------- /k8s-source/adapters/secret_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var secretYAML = ` 8 | apiVersion: v1 9 | kind: Secret 10 | metadata: 11 | name: secret-test-secret 12 | type: Opaque 13 | data: 14 | username: dXNlcm5hbWUx # base64-encoded "username1" 15 | password: cGFzc3dvcmQx # base64-encoded "password1" 16 | 17 | ` 18 | 19 | func TestSecretAdapter(t *testing.T) { 20 | sd := ScopeDetails{ 21 | ClusterName: CurrentCluster.Name, 22 | Namespace: "default", 23 | } 24 | 25 | adapter := newSecretAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 26 | 27 | st := AdapterTests{ 28 | Adapter: adapter, 29 | GetQuery: "secret-test-secret", 30 | GetScope: sd.String(), 31 | SetupYAML: secretYAML, 32 | } 33 | 34 | st.Execute(t) 35 | } 36 | -------------------------------------------------------------------------------- /k8s-source/adapters/serviceaccount_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | ) 8 | 9 | var serviceAccountYAML = ` 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: service-account-secret 14 | type: Opaque 15 | data: 16 | username: Zm9vCg== 17 | password: Zm9vCg== 18 | --- 19 | apiVersion: v1 20 | kind: Secret 21 | metadata: 22 | name: service-account-secret-pull 23 | type: kubernetes.io/dockerconfigjson 24 | data: 25 | .dockerconfigjson: eyJhdXRocyI6eyJnaGNyLmlvIjp7InVzZXJuYW1lIjoiaHVudGVyIiwicGFzc3dvcmQiOiJodW50ZXIyIiwiZW1haWwiOiJmb29AYmFyLmNvbSIsImF1dGgiOiJhSFZ1ZEdWeU9taDFiblJsY2pJPSJ9fX0= 26 | --- 27 | apiVersion: v1 28 | kind: ServiceAccount 29 | metadata: 30 | name: test-service-account 31 | secrets: 32 | - name: service-account-secret 33 | imagePullSecrets: 34 | - name: service-account-secret-pull 35 | ` 36 | 37 | func TestServiceAccountAdapter(t *testing.T) { 38 | sd := ScopeDetails{ 39 | ClusterName: CurrentCluster.Name, 40 | Namespace: "default", 41 | } 42 | 43 | adapter := newServiceAccountAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 44 | 45 | st := AdapterTests{ 46 | Adapter: adapter, 47 | GetQuery: "test-service-account", 48 | GetScope: sd.String(), 49 | SetupYAML: serviceAccountYAML, 50 | GetQueryTests: QueryTests{ 51 | { 52 | ExpectedType: "Secret", 53 | ExpectedMethod: sdp.QueryMethod_GET, 54 | ExpectedQuery: "service-account-secret", 55 | ExpectedScope: sd.String(), 56 | }, 57 | { 58 | ExpectedType: "Secret", 59 | ExpectedMethod: sdp.QueryMethod_GET, 60 | ExpectedQuery: "service-account-secret-pull", 61 | ExpectedScope: sd.String(), 62 | }, 63 | }, 64 | } 65 | 66 | st.Execute(t) 67 | } 68 | -------------------------------------------------------------------------------- /k8s-source/adapters/storageclass.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "github.com/overmindtech/cli/discovery" 5 | "github.com/overmindtech/cli/sdp-go" 6 | v1 "k8s.io/api/storage/v1" 7 | 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | func newStorageClassAdapter(cs *kubernetes.Clientset, cluster string, namespaces []string) discovery.ListableAdapter { 12 | return &KubeTypeAdapter[*v1.StorageClass, *v1.StorageClassList]{ 13 | ClusterName: cluster, 14 | Namespaces: namespaces, 15 | TypeName: "StorageClass", 16 | ClusterInterfaceBuilder: func() ItemInterface[*v1.StorageClass, *v1.StorageClassList] { 17 | return cs.StorageV1().StorageClasses() 18 | }, 19 | ListExtractor: func(list *v1.StorageClassList) ([]*v1.StorageClass, error) { 20 | extracted := make([]*v1.StorageClass, len(list.Items)) 21 | 22 | for i := range list.Items { 23 | extracted[i] = &list.Items[i] 24 | } 25 | 26 | return extracted, nil 27 | }, 28 | AdapterMetadata: storageClassAdapterMetadata, 29 | } 30 | } 31 | 32 | var storageClassAdapterMetadata = Metadata.Register(&sdp.AdapterMetadata{ 33 | Type: "StorageClass", 34 | DescriptiveName: "Storage Class", 35 | Category: sdp.AdapterCategory_ADAPTER_CATEGORY_STORAGE, 36 | SupportedQueryMethods: DefaultSupportedQueryMethods("Storage Class"), 37 | TerraformMappings: []*sdp.TerraformMapping{ 38 | { 39 | TerraformMethod: sdp.QueryMethod_GET, 40 | TerraformQueryMap: "kubernetes_storage_class.metadata[0].name", 41 | }, 42 | { 43 | TerraformMethod: sdp.QueryMethod_GET, 44 | TerraformQueryMap: "kubernetes_storage_class_v1.metadata[0].name", 45 | }, 46 | }, 47 | }) 48 | 49 | func init() { 50 | registerAdapterLoader(newStorageClassAdapter) 51 | } 52 | -------------------------------------------------------------------------------- /k8s-source/adapters/storageclass_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var storageClassYAML = ` 8 | apiVersion: storage.k8s.io/v1 9 | kind: StorageClass 10 | metadata: 11 | name: storage-class-test 12 | provisioner: kubernetes.io/aws-ebs 13 | parameters: 14 | type: gp2 15 | 16 | ` 17 | 18 | func TestStorageClassAdapter(t *testing.T) { 19 | sd := ScopeDetails{ 20 | ClusterName: CurrentCluster.Name, 21 | Namespace: "default", 22 | } 23 | 24 | adapter := newStorageClassAdapter(CurrentCluster.ClientSet, sd.ClusterName, []string{sd.Namespace}) 25 | 26 | st := AdapterTests{ 27 | Adapter: adapter, 28 | GetQuery: "storage-class-test", 29 | GetScope: sd.String(), 30 | SetupYAML: storageClassYAML, 31 | } 32 | 33 | st.Execute(t) 34 | } 35 | -------------------------------------------------------------------------------- /k8s-source/build/package/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the source binary 2 | FROM golang:1.24-alpine AS builder 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | ARG BUILD_VERSION 6 | ARG BUILD_COMMIT 7 | 8 | # required for accessing the private dependencies and generating version descriptor 9 | RUN apk add --no-cache git curl 10 | 11 | WORKDIR /workspace 12 | 13 | # Copy the go source 14 | COPY . . 15 | 16 | # Build 17 | RUN --mount=type=cache,target=/go/pkg \ 18 | --mount=type=cache,target=/root/.cache/go-build \ 19 | GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags="-s -w -X github.com/overmindtech/cli/tracing.version=${BUILD_VERSION} -X github.com/overmindtech/cli/tracing.commit=${BUILD_COMMIT}" -o source k8s-source/main.go 20 | 21 | FROM alpine:3.21 22 | WORKDIR / 23 | COPY --from=builder /workspace/source . 24 | USER 65534:65534 25 | 26 | ENTRYPOINT ["/source"] 27 | -------------------------------------------------------------------------------- /k8s-source/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "auths": { 3 | "ghcr.io": {}, 4 | }, 5 | "credsStore": "desktop" 6 | } 7 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: overmind-kube-source 3 | description: A source that allows Overmind to read from the current Kubernetes cluster 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | # 19 | # This is set during CI 20 | version: 0.0.0 21 | 22 | # This is the version number of the application being deployed. This version number should be 23 | # incremented each time you make changes to the application. Versions are not expected to 24 | # follow Semantic Versioning. They should reflect the version the application is using. 25 | # It is recommended to use it with quotes. 26 | # 27 | # This is set during CI 28 | appVersion: "0.0.0" 29 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/README.md: -------------------------------------------------------------------------------- 1 | # K8s Source Helm Chart 2 | 3 | ## Developing 4 | 5 | Installing into a local cluster: 6 | 7 | ```shell 8 | helm install k8s-source deployments/overmind-kube-source --set source.natsJWT=REPLACEME,source.natsNKeySeed=REPLACEME 9 | ``` 10 | 11 | Removing the chart: 12 | 13 | ```shell 14 | helm uninstall k8s-source 15 | ``` 16 | 17 | ## Releasing 18 | 19 | These charts are automatically released and pushed to Cloudsmith when the monorepo is tagged with a version in the following format `k8s-source/v1.2.3`. This will cause the docker container to be built, tagged with `1.2.3`, pushed, and a new corresponding helm chart released. See `.github/workflows/k8s-source-release.yml` for more details -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | The overmind source has now been installed ✅ -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: {{ include "overmind-kube-source.clusterRoleName" . }} 5 | rules: 6 | - apiGroups: ["*"] 7 | resources: ["*"] 8 | verbs: ["get", "list", "watch"] 9 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: {{ include "overmind-kube-source.clusterRoleBindingName" . }} 5 | subjects: 6 | - kind: ServiceAccount 7 | name: {{ include "overmind-kube-source.serviceAccountName" . }} 8 | namespace: {{ .Release.Namespace }} 9 | roleRef: 10 | kind: ClusterRole 11 | name: {{ include "overmind-kube-source.clusterRoleName" . }} 12 | apiGroup: rbac.authorization.k8s.io 13 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # ConfigMap definition 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "overmind-kube-source.fullname" . }}-config 7 | data: 8 | LOG: {{ .Values.source.log }} 9 | MAX_PARALLEL: {{ .Values.source.maxParallel | quote }} 10 | SOURCE_NAME: {{ .Chart.Name }} 11 | RATE_LIMIT_QPS: {{ .Values.source.rateLimitQPS | quote }} 12 | RATE_LIMIT_BURST: {{ .Values.source.rateLimitBurst | quote }} 13 | {{- if .Values.source.clusterName }} 14 | CLUSTER_NAME: {{ .Values.source.clusterName | quote }} 15 | {{- end }} 16 | {{- if .Values.source.app }} 17 | APP: {{ .Values.source.app | quote }} 18 | {{- end }} 19 | {{- if .Values.source.honeycombApiKey }} 20 | HONEYCOMB_API_KEY: {{ .Values.source.honeycombApiKey | quote }} 21 | {{- end }} 22 | --- 23 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "overmind-kube-source.fullname" . }} 6 | labels: 7 | {{- include "overmind-kube-source.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "overmind-kube-source.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | target: 21 | type: Utilization 22 | averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 23 | {{- end }} 24 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 25 | - type: Resource 26 | resource: 27 | name: memory 28 | target: 29 | type: Utilization 30 | averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 31 | {{- end }} 32 | {{- end }} 33 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.source.apiKey.value }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ include "overmind-kube-source.fullname" . }}-secrets 6 | type: Opaque 7 | data: 8 | API_KEY: {{ .Values.source.apiKey.value | b64enc }} 9 | {{- end }} 10 | -------------------------------------------------------------------------------- /k8s-source/deployments/overmind-kube-source/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ include "overmind-kube-source.serviceAccountName" . }} 5 | labels: 6 | {{- include "overmind-kube-source.labels" . | nindent 4 }} 7 | {{- with .Values.serviceAccount.annotations }} 8 | annotations: 9 | {{- toYaml . | nindent 4 }} 10 | {{- end }} 11 | -------------------------------------------------------------------------------- /k8s-source/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 Dylan Ratcliffe 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "github.com/overmindtech/cli/k8s-source/cmd" 20 | _ "go.uber.org/automaxprocs" 21 | ) 22 | 23 | func main() { 24 | cmd.Execute() 25 | } 26 | -------------------------------------------------------------------------------- /lostpixel.config.ts: -------------------------------------------------------------------------------- 1 | import { CustomProjectConfig } from 'lost-pixel'; 2 | 3 | export const config: CustomProjectConfig = { 4 | customShots: { 5 | currentShotsPath: "./e2e", 6 | }, 7 | 8 | // Lost Pixel Platform (to use in Platform mode, comment out the OSS mode and uncomment this part ) 9 | lostPixelProjectId: 'cm67zty5r073v6ql0ii68nq5f', 10 | apiKey: process.env.LOST_PIXEL_API_KEY, 11 | }; 12 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/charmbracelet/lipgloss" 5 | "github.com/muesli/termenv" 6 | "github.com/overmindtech/cli/cmd" 7 | ) 8 | 9 | func main() { 10 | // work around lipgloss/termenv integration bug. 11 | // See https://github.com/charmbracelet/lipgloss/issues/73#issuecomment-1144921037 12 | lipgloss.SetHasDarkBackground(termenv.HasDarkBackground()) 13 | 14 | cmd.Execute() 15 | } 16 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # This is a very simple example to deploy a few cheap resources into AWS to test the new `terraform plan` and `terraform apply` subcommands. 2 | 3 | terraform { 4 | required_version = ">= 1.5.0" 5 | 6 | required_providers { 7 | aws = { 8 | source = "hashicorp/aws" 9 | version = ">= 4.56" 10 | } 11 | } 12 | } 13 | 14 | provider "aws" {} 15 | provider "aws" { 16 | alias = "aliased" 17 | region = "us-east-1" 18 | } 19 | 20 | variable "bucket_postfix" { 21 | type = string 22 | description = "The prefix to apply to the bucket name." 23 | default = "test" 24 | } 25 | 26 | module "bucket" { 27 | source = "terraform-aws-modules/s3-bucket/aws" 28 | version = "~> 4.0" 29 | 30 | bucket_prefix = "cli-test${var.bucket_postfix}" 31 | 32 | control_object_ownership = true 33 | object_ownership = "BucketOwnerEnforced" 34 | block_public_policy = true 35 | block_public_acls = true 36 | ignore_public_acls = true 37 | restrict_public_buckets = true 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "lost-pixel": "lost-pixel", 8 | "lost-pixel:update": "lost-pixel update" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@types/node": "^22.0.0", 15 | "lost-pixel": "^3.17.0" 16 | }, 17 | "engines": { 18 | "node": "^22.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sdp-go/.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /sdp-go/account.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import "github.com/google/uuid" 4 | 5 | func (a *SourceMetadata) GetUUIDParsed() *uuid.UUID { 6 | u, err := uuid.FromBytes(a.GetUUID()) 7 | if err != nil { 8 | return nil 9 | } 10 | return &u 11 | } 12 | 13 | func (a *SourceHealth) GetUUIDParsed() *uuid.UUID { 14 | u, err := uuid.FromBytes(a.GetUUID()) 15 | if err != nil { 16 | return nil 17 | } 18 | return &u 19 | } 20 | -------------------------------------------------------------------------------- /sdp-go/apikey.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import "github.com/google/uuid" 4 | 5 | func (a *APIKeyMetadata) GetUUIDParsed() *uuid.UUID { 6 | u, err := uuid.FromBytes(a.GetUuid()) 7 | if err != nil { 8 | return nil 9 | } 10 | return &u 11 | } 12 | -------------------------------------------------------------------------------- /sdp-go/bookmarks.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | func (b *Bookmark) ToMap() map[string]any { 4 | return map[string]any{ 5 | "metadata": b.GetMetadata().ToMap(), 6 | "properties": b.GetProperties().ToMap(), 7 | } 8 | } 9 | 10 | func (bm *BookmarkMetadata) ToMap() map[string]any { 11 | return map[string]any{ 12 | "UUID": stringFromUuidBytes(bm.GetUUID()), 13 | "created": bm.GetCreated().AsTime(), 14 | } 15 | } 16 | 17 | func (bp *BookmarkProperties) ToMap() map[string]any { 18 | return map[string]any{ 19 | "name": bp.GetName(), 20 | "description": bp.GetDescription(), 21 | "queries": bp.GetQueries(), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sdp-go/changetimeline.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | type ChangeTimelineEntryV2Name string 4 | 5 | // If you add/delete/move an entry here, make sure to update/check the following: 6 | // - the PopulateChangeTimelineV2 function 7 | // - GetChangeTimelineV2 in api-server/server/changesservice.go 8 | // - resetChangeAnalysisTables in api-server/server/changeanalysis/shared.go 9 | // - the cli tool if we are waiting for a change analysis to finish 10 | const ( 11 | ChangeTimelineEntryV2NameChangeCreated ChangeTimelineEntryV2Name = "Change Created" 12 | ChangeTimelineEntryV2NameMappedResources ChangeTimelineEntryV2Name = "Mapped Resources" 13 | ChangeTimelineEntryV2NameCalculatedBlastRadius ChangeTimelineEntryV2Name = "Calculated Blast Radius" 14 | ChangeTimelineEntryV2NameCalculatedRisks ChangeTimelineEntryV2Name = "Calculated Risks" 15 | ChangeTimelineEntryV2NameAutoTagging ChangeTimelineEntryV2Name = "Auto Tagging" 16 | ChangeTimelineEntryV2NameChangeStarted ChangeTimelineEntryV2Name = "Change Started" 17 | ChangeTimelineEntryV2NameChangeFinished ChangeTimelineEntryV2Name = "Change Finished" 18 | ) 19 | -------------------------------------------------------------------------------- /sdp-go/compare.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import "fmt" 4 | 5 | // Comparer is an object that can be compared for the purposes of sorting. 6 | // Basically anything that implements this interface is sortable 7 | type Comparer interface { 8 | Compare(b *Item) int 9 | } 10 | 11 | // Compare compares two Items for the purposes of sorting. This sorts based on 12 | // the string conversion of the Type, followed by the UniqueAttribute 13 | func (i *Item) Compare(r *Item) (int, error) { 14 | // Convert to strings 15 | right := fmt.Sprintf("%v: %v", r.GetType(), r.UniqueAttributeValue()) 16 | left := fmt.Sprintf("%v: %v", i.GetType(), i.UniqueAttributeValue()) 17 | 18 | // Compare the strings and return the value 19 | switch { 20 | case left > right: 21 | return 1, nil 22 | case left < right: 23 | return -1, nil 24 | default: 25 | return 0, nil 26 | } 27 | } 28 | 29 | // CompareError is returned when two Items cannot be compared because their 30 | // UniqueAttributeValue() is not sortable 31 | type CompareError Item 32 | 33 | // Error returns the string when the error is handled 34 | func (c *CompareError) Error() string { 35 | return (fmt.Sprintf( 36 | "Item %v unique attribute: %v of type %v does not implement interface fmt.Stringer. Cannot sort.", 37 | c.Type, 38 | c.UniqueAttribute, 39 | c.Type, 40 | )) 41 | } 42 | -------------------------------------------------------------------------------- /sdp-go/connection_test.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | // This is an example of a Query with a timeout. This attribute was removed and 9 | // replaced with a `reserved` field. Therefore it is being used to test how we 10 | // handle older messages 11 | var exampleRemovedAttribute = []byte{ 12 | 0xa, 0x3, 0x66, 0x6f, 0x6f, 0x1a, 0x3, 0x66, 0x6f, 0x6f, 0x22, 0x4, 0x8, 13 | 0xa, 0x10, 0x1, 0x2a, 0x3, 0x66, 0x6f, 0x6f, 0x30, 0x1, 0x3a, 0x10, 0x4e, 14 | 0x43, 0x68, 0xd9, 0x17, 0xd4, 0x4d, 0x83, 0xa9, 0xe6, 0xf5, 0x3a, 0xec, 15 | 0xc7, 0xe7, 0xf0, 0x42, 0x2, 0x8, 0xa, 16 | } 17 | 18 | func TestUnmarshal(t *testing.T) { 19 | ctx := context.Background() 20 | query := new(Query) 21 | 22 | err := Unmarshal(ctx, exampleRemovedAttribute, query) 23 | 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sdp-go/errors.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/google/uuid" 8 | ) 9 | 10 | const ErrorTemplate string = `%v 11 | 12 | ErrorType: %v 13 | Scope: %v 14 | SourceName: %v 15 | ItemType: %v 16 | ResponderName: %v` 17 | 18 | // assert interface 19 | var _ error = (*QueryError)(nil) 20 | 21 | func (e *QueryError) GetUUIDParsed() *uuid.UUID { 22 | u, err := uuid.FromBytes(e.GetUUID()) 23 | if err != nil { 24 | return nil 25 | } 26 | return &u 27 | } 28 | 29 | // Ensure that the QueryError is seen as a valid error in golang 30 | func (e *QueryError) Error() string { 31 | return fmt.Sprintf( 32 | ErrorTemplate, 33 | e.GetErrorString(), 34 | e.GetErrorType().String(), 35 | e.GetScope(), 36 | e.GetSourceName(), 37 | e.GetItemType(), 38 | e.GetResponderName(), 39 | ) 40 | } 41 | 42 | // NewQueryError converts a regular error to a QueryError of type 43 | // OTHER. If the input error is already a QueryError then it is preserved 44 | func NewQueryError(err error) *QueryError { 45 | var sdpErr *QueryError 46 | if errors.As(err, &sdpErr) { 47 | return sdpErr 48 | } 49 | 50 | return &QueryError{ 51 | ErrorType: QueryError_OTHER, 52 | ErrorString: err.Error(), 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sdp-go/handler_cancelquery.go: -------------------------------------------------------------------------------- 1 | // Code generated by "genhandler CancelQuery"; DO NOT EDIT 2 | 3 | package sdp 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/nats-io/nats.go" 9 | "github.com/overmindtech/cli/tracing" 10 | "go.opentelemetry.io/otel/trace" 11 | ) 12 | 13 | func NewCancelQueryHandler(spanName string, h func(ctx context.Context, i *CancelQuery), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 14 | return NewOtelExtractingHandler( 15 | spanName, 16 | func(ctx context.Context, m *nats.Msg) { 17 | var i CancelQuery 18 | err := Unmarshal(ctx, m.Data, &i) 19 | if err != nil { 20 | return 21 | } 22 | h(ctx, &i) 23 | }, 24 | tracing.Tracer(), 25 | ) 26 | } 27 | 28 | func NewRawCancelQueryHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *CancelQuery), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 29 | return NewOtelExtractingHandler( 30 | spanName, 31 | func(ctx context.Context, m *nats.Msg) { 32 | var i CancelQuery 33 | err := Unmarshal(ctx, m.Data, &i) 34 | if err != nil { 35 | return 36 | } 37 | h(ctx, m, &i) 38 | }, 39 | tracing.Tracer(), 40 | ) 41 | } 42 | 43 | func NewAsyncRawCancelQueryHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *CancelQuery), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 44 | return NewAsyncOtelExtractingHandler( 45 | spanName, 46 | func(ctx context.Context, m *nats.Msg) { 47 | var i CancelQuery 48 | err := Unmarshal(ctx, m.Data, &i) 49 | if err != nil { 50 | return 51 | } 52 | h(ctx, m, &i) 53 | }, 54 | tracing.Tracer(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /sdp-go/handler_gatewayresponse.go: -------------------------------------------------------------------------------- 1 | // Code generated by "genhandler GatewayResponse"; DO NOT EDIT 2 | 3 | package sdp 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/nats-io/nats.go" 9 | "github.com/overmindtech/cli/tracing" 10 | "go.opentelemetry.io/otel/trace" 11 | ) 12 | 13 | func NewGatewayResponseHandler(spanName string, h func(ctx context.Context, i *GatewayResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 14 | return NewOtelExtractingHandler( 15 | spanName, 16 | func(ctx context.Context, m *nats.Msg) { 17 | var i GatewayResponse 18 | err := Unmarshal(ctx, m.Data, &i) 19 | if err != nil { 20 | return 21 | } 22 | h(ctx, &i) 23 | }, 24 | tracing.Tracer(), 25 | ) 26 | } 27 | 28 | func NewRawGatewayResponseHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *GatewayResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 29 | return NewOtelExtractingHandler( 30 | spanName, 31 | func(ctx context.Context, m *nats.Msg) { 32 | var i GatewayResponse 33 | err := Unmarshal(ctx, m.Data, &i) 34 | if err != nil { 35 | return 36 | } 37 | h(ctx, m, &i) 38 | }, 39 | tracing.Tracer(), 40 | ) 41 | } 42 | 43 | func NewAsyncRawGatewayResponseHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *GatewayResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 44 | return NewAsyncOtelExtractingHandler( 45 | spanName, 46 | func(ctx context.Context, m *nats.Msg) { 47 | var i GatewayResponse 48 | err := Unmarshal(ctx, m.Data, &i) 49 | if err != nil { 50 | return 51 | } 52 | h(ctx, m, &i) 53 | }, 54 | tracing.Tracer(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /sdp-go/handler_natsgetlogrecordsrequest.go: -------------------------------------------------------------------------------- 1 | // Code generated by "genhandler NATSGetLogRecordsRequest"; DO NOT EDIT 2 | 3 | package sdp 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/nats-io/nats.go" 9 | "github.com/overmindtech/cli/tracing" 10 | "go.opentelemetry.io/otel/trace" 11 | ) 12 | 13 | func NewNATSGetLogRecordsRequestHandler(spanName string, h func(ctx context.Context, i *NATSGetLogRecordsRequest), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 14 | return NewOtelExtractingHandler( 15 | spanName, 16 | func(ctx context.Context, m *nats.Msg) { 17 | var i NATSGetLogRecordsRequest 18 | err := Unmarshal(ctx, m.Data, &i) 19 | if err != nil { 20 | return 21 | } 22 | h(ctx, &i) 23 | }, 24 | tracing.Tracer(), 25 | ) 26 | } 27 | 28 | func NewRawNATSGetLogRecordsRequestHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *NATSGetLogRecordsRequest), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 29 | return NewOtelExtractingHandler( 30 | spanName, 31 | func(ctx context.Context, m *nats.Msg) { 32 | var i NATSGetLogRecordsRequest 33 | err := Unmarshal(ctx, m.Data, &i) 34 | if err != nil { 35 | return 36 | } 37 | h(ctx, m, &i) 38 | }, 39 | tracing.Tracer(), 40 | ) 41 | } 42 | 43 | func NewAsyncRawNATSGetLogRecordsRequestHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *NATSGetLogRecordsRequest), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 44 | return NewAsyncOtelExtractingHandler( 45 | spanName, 46 | func(ctx context.Context, m *nats.Msg) { 47 | var i NATSGetLogRecordsRequest 48 | err := Unmarshal(ctx, m.Data, &i) 49 | if err != nil { 50 | return 51 | } 52 | h(ctx, m, &i) 53 | }, 54 | tracing.Tracer(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /sdp-go/handler_natsgetlogrecordsresponse.go: -------------------------------------------------------------------------------- 1 | // Code generated by "genhandler NATSGetLogRecordsResponse"; DO NOT EDIT 2 | 3 | package sdp 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/nats-io/nats.go" 9 | "github.com/overmindtech/cli/tracing" 10 | "go.opentelemetry.io/otel/trace" 11 | ) 12 | 13 | func NewNATSGetLogRecordsResponseHandler(spanName string, h func(ctx context.Context, i *NATSGetLogRecordsResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 14 | return NewOtelExtractingHandler( 15 | spanName, 16 | func(ctx context.Context, m *nats.Msg) { 17 | var i NATSGetLogRecordsResponse 18 | err := Unmarshal(ctx, m.Data, &i) 19 | if err != nil { 20 | return 21 | } 22 | h(ctx, &i) 23 | }, 24 | tracing.Tracer(), 25 | ) 26 | } 27 | 28 | func NewRawNATSGetLogRecordsResponseHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *NATSGetLogRecordsResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 29 | return NewOtelExtractingHandler( 30 | spanName, 31 | func(ctx context.Context, m *nats.Msg) { 32 | var i NATSGetLogRecordsResponse 33 | err := Unmarshal(ctx, m.Data, &i) 34 | if err != nil { 35 | return 36 | } 37 | h(ctx, m, &i) 38 | }, 39 | tracing.Tracer(), 40 | ) 41 | } 42 | 43 | func NewAsyncRawNATSGetLogRecordsResponseHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *NATSGetLogRecordsResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 44 | return NewAsyncOtelExtractingHandler( 45 | spanName, 46 | func(ctx context.Context, m *nats.Msg) { 47 | var i NATSGetLogRecordsResponse 48 | err := Unmarshal(ctx, m.Data, &i) 49 | if err != nil { 50 | return 51 | } 52 | h(ctx, m, &i) 53 | }, 54 | tracing.Tracer(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /sdp-go/handler_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by "genhandler Query"; DO NOT EDIT 2 | 3 | package sdp 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/nats-io/nats.go" 9 | "github.com/overmindtech/cli/tracing" 10 | "go.opentelemetry.io/otel/trace" 11 | ) 12 | 13 | func NewQueryHandler(spanName string, h func(ctx context.Context, i *Query), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 14 | return NewOtelExtractingHandler( 15 | spanName, 16 | func(ctx context.Context, m *nats.Msg) { 17 | var i Query 18 | err := Unmarshal(ctx, m.Data, &i) 19 | if err != nil { 20 | return 21 | } 22 | h(ctx, &i) 23 | }, 24 | tracing.Tracer(), 25 | ) 26 | } 27 | 28 | func NewRawQueryHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *Query), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 29 | return NewOtelExtractingHandler( 30 | spanName, 31 | func(ctx context.Context, m *nats.Msg) { 32 | var i Query 33 | err := Unmarshal(ctx, m.Data, &i) 34 | if err != nil { 35 | return 36 | } 37 | h(ctx, m, &i) 38 | }, 39 | tracing.Tracer(), 40 | ) 41 | } 42 | 43 | func NewAsyncRawQueryHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *Query), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 44 | return NewAsyncOtelExtractingHandler( 45 | spanName, 46 | func(ctx context.Context, m *nats.Msg) { 47 | var i Query 48 | err := Unmarshal(ctx, m.Data, &i) 49 | if err != nil { 50 | return 51 | } 52 | h(ctx, m, &i) 53 | }, 54 | tracing.Tracer(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /sdp-go/handler_queryresponse.go: -------------------------------------------------------------------------------- 1 | // Code generated by "genhandler QueryResponse"; DO NOT EDIT 2 | 3 | package sdp 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/nats-io/nats.go" 9 | "github.com/overmindtech/cli/tracing" 10 | "go.opentelemetry.io/otel/trace" 11 | ) 12 | 13 | func NewQueryResponseHandler(spanName string, h func(ctx context.Context, i *QueryResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 14 | return NewOtelExtractingHandler( 15 | spanName, 16 | func(ctx context.Context, m *nats.Msg) { 17 | var i QueryResponse 18 | err := Unmarshal(ctx, m.Data, &i) 19 | if err != nil { 20 | return 21 | } 22 | h(ctx, &i) 23 | }, 24 | tracing.Tracer(), 25 | ) 26 | } 27 | 28 | func NewRawQueryResponseHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *QueryResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 29 | return NewOtelExtractingHandler( 30 | spanName, 31 | func(ctx context.Context, m *nats.Msg) { 32 | var i QueryResponse 33 | err := Unmarshal(ctx, m.Data, &i) 34 | if err != nil { 35 | return 36 | } 37 | h(ctx, m, &i) 38 | }, 39 | tracing.Tracer(), 40 | ) 41 | } 42 | 43 | func NewAsyncRawQueryResponseHandler(spanName string, h func(ctx context.Context, m *nats.Msg, i *QueryResponse), spanOpts ...trace.SpanStartOption) nats.MsgHandler { 44 | return NewAsyncOtelExtractingHandler( 45 | spanName, 46 | func(ctx context.Context, m *nats.Msg) { 47 | var i QueryResponse 48 | err := Unmarshal(ctx, m.Data, &i) 49 | if err != nil { 50 | return 51 | } 52 | h(ctx, m, &i) 53 | }, 54 | tracing.Tracer(), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /sdp-go/responses.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | // TODO: instead of translating, unify this 4 | func (r *Response) ToQueryStatus() *QueryStatus { 5 | return &QueryStatus{ 6 | UUID: r.GetUUID(), 7 | Status: r.GetState().ToQueryStatus(), 8 | } 9 | } 10 | 11 | // TODO: instead of translating, unify this 12 | func (r ResponderState) ToQueryStatus() QueryStatus_Status { 13 | switch r { 14 | case ResponderState_WORKING: 15 | return QueryStatus_STARTED 16 | case ResponderState_COMPLETE: 17 | return QueryStatus_FINISHED 18 | case ResponderState_ERROR: 19 | return QueryStatus_ERRORED 20 | case ResponderState_CANCELLED: 21 | return QueryStatus_CANCELLED 22 | case ResponderState_STALLED: 23 | return QueryStatus_ERRORED 24 | default: 25 | return QueryStatus_UNSPECIFIED 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sdp-go/snapshots.go: -------------------------------------------------------------------------------- 1 | package sdp 2 | 3 | func (s *Snapshot) ToMap() map[string]any { 4 | return map[string]any{ 5 | "metadata": s.GetMetadata().ToMap(), 6 | "properties": s.GetProperties().ToMap(), 7 | } 8 | } 9 | 10 | func (sm *SnapshotMetadata) ToMap() map[string]any { 11 | return map[string]any{ 12 | "UUID": stringFromUuidBytes(sm.GetUUID()), 13 | "created": sm.GetCreated().AsTime(), 14 | } 15 | } 16 | 17 | func (sp *SnapshotProperties) ToMap() map[string]any { 18 | return map[string]any{ 19 | "name": sp.GetName(), 20 | "description": sp.GetDescription(), 21 | "queries": sp.GetQueries(), 22 | "Items": sp.GetItems(), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sdp-go/tracing/main.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | _ "embed" 5 | 6 | "go.opentelemetry.io/otel" 7 | semconv "go.opentelemetry.io/otel/semconv/v1.26.0" 8 | "go.opentelemetry.io/otel/trace" 9 | ) 10 | 11 | const instrumentationName = "github.com/overmindtech/cli/sdp-go" 12 | 13 | var ( 14 | tracer = otel.GetTracerProvider().Tracer( 15 | instrumentationName, 16 | trace.WithSchemaURL(semconv.SchemaURL), 17 | ) 18 | ) 19 | 20 | func Tracer() trace.Tracer { 21 | return tracer 22 | } 23 | -------------------------------------------------------------------------------- /sources/aws/base.go: -------------------------------------------------------------------------------- 1 | package aws 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | "github.com/overmindtech/cli/sources/shared" 8 | ) 9 | 10 | type Base struct { 11 | accountID string 12 | region string 13 | 14 | *shared.Base 15 | } 16 | 17 | func NewBase( 18 | accountID string, 19 | region string, 20 | category sdp.AdapterCategory, 21 | item shared.ItemType, 22 | ) *Base { 23 | return &Base{ 24 | accountID: accountID, 25 | region: region, 26 | Base: shared.NewBase( 27 | category, 28 | item, 29 | []string{fmt.Sprintf("%s.%s", accountID, region)}, 30 | ), 31 | } 32 | } 33 | 34 | func (m *Base) AccountID() string { 35 | return m.accountID 36 | } 37 | 38 | func (m *Base) Region() string { 39 | return m.region 40 | } 41 | -------------------------------------------------------------------------------- /sources/aws/errors.go: -------------------------------------------------------------------------------- 1 | package aws 2 | 3 | import ( 4 | "errors" 5 | "slices" 6 | 7 | awsHttp "github.com/aws/smithy-go/transport/http" 8 | 9 | "github.com/overmindtech/cli/sdp-go" 10 | ) 11 | 12 | // queryError takes an error and returns a sdp.QueryError. 13 | func queryError(err error) *sdp.QueryError { 14 | var responseErr *awsHttp.ResponseError 15 | if errors.As(err, &responseErr) { 16 | // If the input is bad, access is denied, or the thing wasn't found then 17 | // we should assume that it is not exist for this adapter 18 | if slices.Contains([]int{400, 403, 404}, responseErr.HTTPStatusCode()) { 19 | return &sdp.QueryError{ 20 | ErrorType: sdp.QueryError_NOTFOUND, 21 | ErrorString: err.Error(), 22 | } 23 | } 24 | } 25 | 26 | return &sdp.QueryError{ 27 | ErrorType: sdp.QueryError_OTHER, 28 | ErrorString: err.Error(), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sources/aws/shared/models.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "github.com/overmindtech/cli/sources/shared" 5 | ) 6 | 7 | const ( 8 | AWS shared.Source = "aws" 9 | ) 10 | 11 | // APIs 12 | const ( 13 | APIGateway shared.API = "api-gateway" 14 | WAFv2 shared.API = "wafv2" 15 | ) 16 | 17 | // Resources 18 | const ( 19 | APIKey shared.Resource = "api-key" 20 | Stage shared.Resource = "stage" 21 | RESTAPI shared.Resource = "rest-api" 22 | Deployment shared.Resource = "deployment" 23 | WebACL shared.Resource = "web-acl" 24 | ) 25 | -------------------------------------------------------------------------------- /sources/aws/validation_test.go: -------------------------------------------------------------------------------- 1 | package aws 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/overmindtech/cli/discovery" 10 | "github.com/overmindtech/cli/sources" 11 | ) 12 | 13 | type Validate interface { 14 | Validate() error 15 | } 16 | 17 | func TestAdaptersValidation(t *testing.T) { 18 | accountID := "123456789012" 19 | region := "us-east-1" 20 | 21 | var adapters []discovery.Adapter 22 | adapters = append(adapters, 23 | sources.WrapperToAdapter(NewAPIGatewayStage(nil, accountID, region)), 24 | sources.WrapperToAdapter(NewApiGatewayAPIKey(nil, accountID, region)), 25 | ) 26 | 27 | for _, adapter := range adapters { 28 | t.Run(adapter.Name(), func(t *testing.T) { 29 | // Test the adapter 30 | a, ok := adapter.(Validate) 31 | if !ok { 32 | t.Fatalf("Adapter %s does not implement Validate", adapter.Name()) 33 | } 34 | 35 | if err := a.Validate(); err != nil { 36 | t.Fatalf("Adapter %s failed validation: %v", adapter.Name(), err) 37 | } 38 | 39 | if strings.EqualFold(os.Getenv("LOG_LEVEL"), "debug") { 40 | // Pretty print the adapter metadata via json 41 | jsonData, err := json.MarshalIndent(adapter.Metadata(), "", " ") 42 | if err != nil { 43 | t.Fatalf("Failed to marshal adapter metadata: %v", err) 44 | } 45 | t.Logf("Adapter %s metadata: %s", adapter.Name(), string(jsonData)) 46 | } 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sources/example/base.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | "github.com/overmindtech/cli/sources/shared" 8 | ) 9 | 10 | // Base customizes the sources.Base struct 11 | // It adds the project ID and zone to the base struct 12 | // and makes them available to concrete wrapper implementations. 13 | type Base struct { 14 | projectID string 15 | zone string 16 | 17 | *shared.Base 18 | } 19 | 20 | // NewBase creates a new Base struct 21 | func NewBase( 22 | projectID string, 23 | zone string, 24 | category sdp.AdapterCategory, 25 | item shared.ItemType, 26 | ) *Base { 27 | return &Base{ 28 | projectID: projectID, 29 | zone: zone, 30 | Base: shared.NewBase( 31 | category, 32 | item, 33 | []string{fmt.Sprintf("%s.%s", projectID, zone)}, 34 | ), 35 | } 36 | } 37 | 38 | // ProjectID returns the project ID 39 | func (m *Base) ProjectID() string { 40 | return m.projectID 41 | } 42 | 43 | // Zone returns the zone 44 | func (m *Base) Zone() string { 45 | return m.zone 46 | } 47 | -------------------------------------------------------------------------------- /sources/example/errors.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | ) 8 | 9 | // queryError is a helper function to convert errors into sdp.QueryError 10 | func queryError(err error) *sdp.QueryError { 11 | if errors.As(err, new(NotFoundError)) { 12 | return &sdp.QueryError{ 13 | ErrorType: sdp.QueryError_NOTFOUND, 14 | ErrorString: err.Error(), 15 | } 16 | } 17 | 18 | return &sdp.QueryError{ 19 | ErrorType: sdp.QueryError_OTHER, 20 | ErrorString: err.Error(), 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sources/example/shared/models.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "github.com/overmindtech/cli/sources/shared" 5 | ) 6 | 7 | const ( 8 | Source shared.Source = "GCP" 9 | ) 10 | 11 | // APIs 12 | const ( 13 | Compute shared.API = "compute" 14 | ) 15 | 16 | // Resources 17 | const ( 18 | Instance shared.Resource = "instance" 19 | Disk shared.Resource = "disk" 20 | Status shared.Resource = "status" 21 | ) 22 | -------------------------------------------------------------------------------- /sources/example/validation_test.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/overmindtech/cli/discovery" 10 | "github.com/overmindtech/cli/sources" 11 | ) 12 | 13 | type Validate interface { 14 | Validate() error 15 | } 16 | 17 | func TestAdaptersValidation(t *testing.T) { 18 | projectID := "123456789012" 19 | zone := "us-east-1" 20 | 21 | var adapters []discovery.Adapter 22 | adapters = append(adapters, 23 | sources.WrapperToAdapter(NewStandardSearchableListable(nil, projectID, zone)), 24 | sources.WrapperToAdapter(NewCustomSearchableListable(nil, projectID, zone)), 25 | ) 26 | 27 | for _, adapter := range adapters { 28 | t.Run(adapter.Name(), func(t *testing.T) { 29 | // Test the adapter 30 | a, ok := adapter.(Validate) 31 | if !ok { 32 | t.Fatalf("Adapter %s does not implement Validate", adapter.Name()) 33 | } 34 | 35 | if err := a.Validate(); err != nil { 36 | t.Fatalf("Adapter %s failed validation: %v", adapter.Name(), err) 37 | } 38 | 39 | if strings.EqualFold(os.Getenv("LOG_LEVEL"), "debug") { 40 | // Pretty print the adapter metadata via json 41 | jsonData, err := json.MarshalIndent(adapter.Metadata(), "", " ") 42 | if err != nil { 43 | t.Fatalf("Failed to marshal adapter metadata: %v", err) 44 | } 45 | t.Logf("Adapter %s metadata: %s", adapter.Name(), string(jsonData)) 46 | } 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sources/gcp/adapters/compute-accelerator-type.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | gcpshared "github.com/overmindtech/cli/sources/gcp/shared" 5 | "github.com/overmindtech/cli/sources/shared" 6 | ) 7 | 8 | var ComputeAcceleratorType = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.AcceleratorType) 9 | -------------------------------------------------------------------------------- /sources/gcp/adapters/compute-machine-type.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | gcpshared "github.com/overmindtech/cli/sources/gcp/shared" 5 | "github.com/overmindtech/cli/sources/shared" 6 | ) 7 | 8 | var ComputeMachineType = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.MachineType) 9 | -------------------------------------------------------------------------------- /sources/gcp/adapters/compute-region-commitment.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | gcpshared "github.com/overmindtech/cli/sources/gcp/shared" 5 | "github.com/overmindtech/cli/sources/shared" 6 | ) 7 | 8 | var ComputeRegionCommitment = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.RegionCommitment) 9 | -------------------------------------------------------------------------------- /sources/gcp/adapters/compute-resource-policy.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | gcpshared "github.com/overmindtech/cli/sources/gcp/shared" 5 | "github.com/overmindtech/cli/sources/shared" 6 | ) 7 | 8 | var ComputeResourcePolicy = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.ResourcePolicy) 9 | -------------------------------------------------------------------------------- /sources/gcp/adapters/network-services-service-binding.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | gcpshared "github.com/overmindtech/cli/sources/gcp/shared" 5 | "github.com/overmindtech/cli/sources/shared" 6 | ) 7 | 8 | var NetworkServicesServiceBinding = shared.NewItemType(gcpshared.GCP, gcpshared.NetworkServices, gcpshared.ServiceBinding) 9 | -------------------------------------------------------------------------------- /sources/gcp/adapters/network-services-service-lb-policy.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | gcpshared "github.com/overmindtech/cli/sources/gcp/shared" 5 | "github.com/overmindtech/cli/sources/shared" 6 | ) 7 | 8 | var NetworkServicesServiceLbPolicy = shared.NewItemType(gcpshared.GCP, gcpshared.NetworkServices, gcpshared.ServiceLbPolicy) 9 | -------------------------------------------------------------------------------- /sources/gcp/integration-tests/main_test.go: -------------------------------------------------------------------------------- 1 | package integrationtests 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | func TestMain(m *testing.M) { 11 | if shouldRunIntegrationTests() { 12 | fmt.Println("Running integration tests") 13 | os.Exit(m.Run()) 14 | } else { 15 | fmt.Println("Skipping integration tests, set RUN_GCP_INTEGRATION_TESTS=true to run them") 16 | os.Exit(0) 17 | } 18 | } 19 | 20 | func shouldRunIntegrationTests() bool { 21 | run, found := os.LookupEnv("RUN_GCP_INTEGRATION_TESTS") 22 | 23 | if !found { 24 | return false 25 | } 26 | 27 | shouldRun, err := strconv.ParseBool(run) 28 | if err != nil { 29 | return false 30 | } 31 | 32 | return shouldRun 33 | } 34 | -------------------------------------------------------------------------------- /sources/gcp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "go.uber.org/automaxprocs" 5 | 6 | "github.com/overmindtech/cli/sources/gcp/cmd" 7 | ) 8 | 9 | func main() { 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /sources/gcp/proc/proc_test.go: -------------------------------------------------------------------------------- 1 | package proc 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestZoneToRegion(t *testing.T) { 8 | tests := []struct { 9 | name string 10 | zone string 11 | expected string 12 | }{ 13 | { 14 | name: "Valid zone with region us-central1-a", 15 | zone: "us-central1-a", 16 | expected: "us-central1", 17 | }, 18 | { 19 | name: "Valid zone with region europe-west1-b", 20 | zone: "europe-west1-b", 21 | expected: "europe-west1", 22 | }, 23 | { 24 | name: "Empty zone", 25 | zone: "", 26 | expected: "", 27 | }, 28 | { 29 | name: "Zone with no dash", 30 | zone: "uscentral1", 31 | expected: "", 32 | }, 33 | } 34 | 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | result := zoneToRegion(tt.zone) 38 | if result != tt.expected { 39 | t.Errorf("zoneToRegion(%q) = %q; expected %q", tt.zone, result, tt.expected) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sources/gcp/shared/errors.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "google.golang.org/grpc/codes" 5 | "google.golang.org/grpc/status" 6 | 7 | "github.com/overmindtech/cli/sdp-go" 8 | ) 9 | 10 | // QueryError is a helper function to convert errors into sdp.QueryError 11 | func QueryError(err error) *sdp.QueryError { 12 | // Check if the error is a gRPC `not_found` error 13 | if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { 14 | return &sdp.QueryError{ 15 | ErrorType: sdp.QueryError_NOTFOUND, 16 | ErrorString: err.Error(), 17 | } 18 | } 19 | 20 | return &sdp.QueryError{ 21 | ErrorType: sdp.QueryError_OTHER, 22 | ErrorString: err.Error(), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sources/gcp/shared/network-security-clients.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "context" 5 | 6 | "cloud.google.com/go/networksecurity/apiv1beta1" 7 | "cloud.google.com/go/networksecurity/apiv1beta1/networksecuritypb" 8 | "github.com/googleapis/gax-go/v2" 9 | ) 10 | 11 | type NetworkSecurityClientTlsPolicyClient interface { 12 | Get(ctx context.Context, req *networksecuritypb.GetClientTlsPolicyRequest, opts ...gax.CallOption) (*networksecuritypb.ClientTlsPolicy, error) 13 | List(ctx context.Context, req *networksecuritypb.ListClientTlsPoliciesRequest, opts ...gax.CallOption) NetworkSecurityClientTlsPolicyIterator 14 | } 15 | 16 | type NetworkSecurityClientTlsPolicyIterator interface { 17 | Next() (*networksecuritypb.ClientTlsPolicy, error) 18 | } 19 | 20 | type networkSecurityClientTlsPolicyClient struct { 21 | client *networksecurity.Client 22 | } 23 | 24 | func (c networkSecurityClientTlsPolicyClient) Get(ctx context.Context, req *networksecuritypb.GetClientTlsPolicyRequest, opts ...gax.CallOption) (*networksecuritypb.ClientTlsPolicy, error) { 25 | return c.client.GetClientTlsPolicy(ctx, req, opts...) 26 | } 27 | 28 | func (c networkSecurityClientTlsPolicyClient) List(ctx context.Context, req *networksecuritypb.ListClientTlsPoliciesRequest, opts ...gax.CallOption) NetworkSecurityClientTlsPolicyIterator { 29 | return c.client.ListClientTlsPolicies(ctx, req, opts...) 30 | } 31 | 32 | // NewNetworkSecurityClientTlsPolicyClient creates a new NetworkSecurityClientTlsPolicyClient 33 | func NewNetworkSecurityClientTlsPolicyClient(client *networksecurity.Client) NetworkSecurityClientTlsPolicyClient { 34 | return &networkSecurityClientTlsPolicyClient{ 35 | client: client, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/shared/util.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/overmindtech/cli/sdp-go" 7 | ) 8 | 9 | // ToAttributesWithExclude converts an interface to SDP attributes using the `sdp.ToAttributesSorted` 10 | // function, and also allows the user to exclude certain top-level fields from 11 | // the resulting attributes 12 | func ToAttributesWithExclude(i interface{}, exclusions ...string) (*sdp.ItemAttributes, error) { 13 | attrs, err := sdp.ToAttributesViaJson(i) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | for _, exclusion := range exclusions { 19 | if s := attrs.GetAttrStruct(); s != nil { 20 | delete(s.GetFields(), exclusion) 21 | } 22 | } 23 | 24 | return attrs, nil 25 | } 26 | 27 | // CompositeLookupKey creates a composite lookup key from multiple query parts. 28 | func CompositeLookupKey(queryParts ...string) string { 29 | // Join the query parts with the default separator "|" 30 | return strings.Join(queryParts, QuerySeparator) 31 | } 32 | -------------------------------------------------------------------------------- /sources/shared/util_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCompositeLookupKey(t *testing.T) { 8 | tests := []struct { 9 | name string 10 | queryParts []string 11 | expected string 12 | }{ 13 | { 14 | name: "Single query part", 15 | queryParts: []string{"part1"}, 16 | expected: "part1", 17 | }, 18 | { 19 | name: "Multiple query parts", 20 | queryParts: []string{"part1", "part2", "part3"}, 21 | expected: "part1|part2|part3", 22 | }, 23 | { 24 | name: "Empty query parts", 25 | queryParts: []string{}, 26 | expected: "", 27 | }, 28 | { 29 | name: "Query parts with empty strings", 30 | queryParts: []string{"part1", "", "part3"}, 31 | expected: "part1||part3", 32 | }, 33 | } 34 | 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | result := CompositeLookupKey(tt.queryParts...) 38 | if result != tt.expected { 39 | t.Errorf("CompositeLookupKey(%v) = %q; want %q", tt.queryParts, result, tt.expected) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sources/stdlib/items.go: -------------------------------------------------------------------------------- 1 | package stdlib 2 | 3 | import ( 4 | "github.com/overmindtech/cli/sources/shared" 5 | stdlibshared "github.com/overmindtech/cli/sources/stdlib/shared" 6 | ) 7 | 8 | var ( 9 | NetworkIP = shared.NewItemType(stdlibshared.Stdlib, stdlibshared.Network, stdlibshared.IP) 10 | ) 11 | -------------------------------------------------------------------------------- /sources/stdlib/shared/models.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "github.com/overmindtech/cli/sources/shared" 4 | 5 | const ( 6 | Stdlib shared.Source = "Stdlib" 7 | ) 8 | 9 | const ( 10 | Network shared.API = "network" 11 | ) 12 | 13 | const ( 14 | IP shared.Resource = "ip" 15 | ) 16 | -------------------------------------------------------------------------------- /sources/transformer_test.go: -------------------------------------------------------------------------------- 1 | package sources 2 | 3 | import ( 4 | "testing" 5 | 6 | aws "github.com/overmindtech/cli/sources/aws/shared" 7 | gcp "github.com/overmindtech/cli/sources/gcp/shared" 8 | "github.com/overmindtech/cli/sources/shared" 9 | ) 10 | 11 | func TestItemTypeReadableFormat(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | input shared.ItemType 15 | expected string 16 | }{ 17 | { 18 | name: "Three parts input", 19 | input: shared.NewItemType(gcp.GCP, gcp.Compute, gcp.Instance), 20 | expected: "GCP Compute Instance", 21 | }, 22 | { 23 | name: "Three parts input", 24 | input: shared.NewItemType(aws.AWS, aws.APIGateway, aws.RESTAPI), 25 | expected: "AWS Api Gateway Rest Api", 26 | // Note that this is only testing the fallback rendering, 27 | // adapter implementors will have to supply a custom descriptive name, 28 | // like "Amazon API Gateway REST API" in the `AdapterMetadata`. 29 | }, 30 | } 31 | 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | actual := tt.input.Readable() 35 | if actual != tt.expected { 36 | t.Errorf("readableFormat(%q) = %q; expected %q", tt.input, actual, tt.expected) 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /stdlib-source/adapters/main_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "github.com/openrdap/rdap" 8 | "github.com/openrdap/rdap/bootstrap" 9 | ) 10 | 11 | func testRdapClient(t *testing.T) *rdap.Client { 12 | return &rdap.Client{ 13 | HTTP: http.DefaultClient, 14 | Bootstrap: &bootstrap.Client{ 15 | Verbose: func(text string) { 16 | t.Log(text) 17 | }, 18 | }, 19 | Verbose: func(text string) { 20 | t.Log(text) 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stdlib-source/adapters/rdap-asn_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openrdap/rdap" 8 | "github.com/overmindtech/cli/sdpcache" 9 | ) 10 | 11 | func TestASNAdapterGet(t *testing.T) { 12 | t.Parallel() 13 | 14 | src := &RdapASNAdapter{ 15 | ClientFac: func() *rdap.Client { return testRdapClient(t) }, 16 | Cache: sdpcache.NewCache(), 17 | } 18 | 19 | item, err := src.Get(context.Background(), "global", "AS15169", false) 20 | 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | err = item.Validate() 26 | 27 | if err != nil { 28 | t.Error(err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /stdlib-source/adapters/rdap-domain_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openrdap/rdap" 8 | "github.com/overmindtech/cli/sdpcache" 9 | ) 10 | 11 | func TestDomainAdapterGet(t *testing.T) { 12 | t.Parallel() 13 | 14 | src := &RdapDomainAdapter{ 15 | ClientFac: func() *rdap.Client { return testRdapClient(t) }, 16 | Cache: sdpcache.NewCache(), 17 | } 18 | 19 | t.Run("without a dot", func(t *testing.T) { 20 | items, err := src.Search(context.Background(), "global", "reddit.map.fastly.net", false) 21 | 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | if len(items) != 1 { 27 | t.Fatal("Expected 1 item") 28 | } 29 | 30 | item := items[0] 31 | 32 | err = item.Validate() 33 | 34 | if err != nil { 35 | t.Error(err) 36 | } 37 | }) 38 | 39 | t.Run("with a dot", func(t *testing.T) { 40 | items, err := src.Search(context.Background(), "global", "reddit.map.fastly.net.", false) 41 | 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | if len(items) != 1 { 47 | t.Fatal("Expected 1 item") 48 | } 49 | 50 | item := items[0] 51 | 52 | err = item.Validate() 53 | 54 | if err != nil { 55 | t.Error(err) 56 | } 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /stdlib-source/adapters/rdap-entity_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/openrdap/rdap" 9 | "github.com/overmindtech/cli/sdp-go" 10 | "github.com/overmindtech/cli/sdpcache" 11 | ) 12 | 13 | func TestEntityAdapterSearch(t *testing.T) { 14 | t.Parallel() 15 | 16 | realUrls := []string{ 17 | "https://rdap.apnic.net/entity/AIC3-AP", 18 | "https://rdap.apnic.net/entity/IRT-APNICRANDNET-AU", 19 | "https://rdap.arin.net/registry/entity/HPINC-Z", 20 | } 21 | 22 | src := &RdapEntityAdapter{ 23 | ClientFac: func() *rdap.Client { return testRdapClient(t) }, 24 | Cache: sdpcache.NewCache(), 25 | } 26 | 27 | for _, realUrl := range realUrls { 28 | t.Run(realUrl, func(t *testing.T) { 29 | items, err := src.Search(context.Background(), "global", realUrl, false) 30 | 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | 35 | if len(items) != 1 { 36 | t.Fatalf("Expected 1 item, got %v", len(items)) 37 | } 38 | 39 | item := items[0] 40 | 41 | err = item.Validate() 42 | 43 | if err != nil { 44 | t.Error(err) 45 | } 46 | }) 47 | } 48 | 49 | t.Run("not found", func(t *testing.T) { 50 | _, err := src.Search(context.Background(), "global", "https://rdap.apnic.net/entity/NOTFOUND", false) 51 | 52 | if err == nil { 53 | t.Fatal("Expected error") 54 | } 55 | 56 | var sdpError *sdp.QueryError 57 | 58 | if ok := errors.As(err, &sdpError); ok { 59 | if sdpError.GetErrorType() != sdp.QueryError_NOTFOUND { 60 | t.Errorf("Expected QueryError_NOTFOUND, got %v", sdpError.GetErrorType()) 61 | } 62 | } else { 63 | t.Fatalf("Expected QueryError, got %T", err) 64 | } 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /stdlib-source/adapters/rdap-nameserver_test.go: -------------------------------------------------------------------------------- 1 | package adapters 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/openrdap/rdap" 8 | "github.com/overmindtech/cli/sdpcache" 9 | ) 10 | 11 | func TestNameserverAdapterSearch(t *testing.T) { 12 | t.Parallel() 13 | 14 | src := &RdapNameserverAdapter{ 15 | ClientFac: func() *rdap.Client { return testRdapClient(t) }, 16 | Cache: sdpcache.NewCache(), 17 | } 18 | 19 | items, err := src.Search(context.Background(), "global", "https://rdap.verisign.com/com/v1/nameserver/NS4.GOOGLE.COM", false) 20 | 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | if len(items) != 1 { 26 | t.Fatal("Expected 1 item") 27 | } 28 | 29 | item := items[0] 30 | 31 | err = item.Validate() 32 | 33 | if err != nil { 34 | t.Error(err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /stdlib-source/build/package/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the source binary 2 | FROM golang:1.24-alpine AS builder 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | ARG BUILD_VERSION 6 | ARG BUILD_COMMIT 7 | 8 | # required for accessing the private dependencies and generating version descriptor 9 | RUN apk add --no-cache git curl 10 | 11 | WORKDIR /workspace 12 | 13 | # Copy the go source 14 | COPY . . 15 | 16 | # Build 17 | RUN --mount=type=cache,target=/go/pkg \ 18 | --mount=type=cache,target=/root/.cache/go-build \ 19 | GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags="-s -w -X github.com/overmindtech/cli/tracing.version=${BUILD_VERSION} -X github.com/overmindtech/cli/tracing.commit=${BUILD_COMMIT}" -o source stdlib-source/main.go 20 | 21 | FROM alpine:3.21 22 | WORKDIR / 23 | COPY --from=builder /workspace/source . 24 | USER 65534:65534 25 | 26 | ENTRYPOINT ["/source"] 27 | -------------------------------------------------------------------------------- /stdlib-source/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 {AUTHOR} 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "github.com/overmindtech/cli/stdlib-source/cmd" 20 | _ "go.uber.org/automaxprocs" 21 | ) 22 | 23 | func main() { 24 | cmd.Execute() 25 | } 26 | -------------------------------------------------------------------------------- /tfutils/testdata/invalid_vars.tfvars: -------------------------------------------------------------------------------- 1 | this is not valid hcl 2 | 3 | And therefore shouldn't parse -------------------------------------------------------------------------------- /tfutils/testdata/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.0", 3 | "terraform_version": "1.5.7", 4 | "values": { 5 | "outputs": {}, 6 | "root_module": { 7 | "resources": [], 8 | "child_modules": [] 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tfutils/testdata/subfolder/more_providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | alias = "subdir" 3 | region = "us-west-2" 4 | access_key = "my-access-key" 5 | secret_key = "my-secret-key" 6 | } -------------------------------------------------------------------------------- /tfutils/testdata/test_vars.tfvars: -------------------------------------------------------------------------------- 1 | # String variable 2 | simple_string="example_string" 3 | 4 | # Number variable 5 | example_number = 42 6 | 7 | # Boolean variable 8 | example_boolean = true 9 | 10 | # List of strings 11 | example_list = ["item1", "item2", "item3"] 12 | 13 | # Map of strings 14 | example_map = { 15 | key1 = "value1" 16 | key2 = "value2" 17 | } 18 | 19 | # Complex map (nested maps) 20 | complex_map = { 21 | nested_map1 = { 22 | nested_key1 = "nested_value1" 23 | nested_key2 = "nested_value2" 24 | } 25 | nested_map2 = { 26 | nested_key1 = "nested_value3" 27 | nested_key2 = "nested_value4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tfutils/testdata/tfvars.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": "example_string", 3 | "list": ["item1", "item2"] 4 | } 5 | -------------------------------------------------------------------------------- /tracing/deferlog.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "runtime/debug" 8 | 9 | "github.com/getsentry/sentry-go" 10 | log "github.com/sirupsen/logrus" 11 | "go.opentelemetry.io/otel/attribute" 12 | "go.opentelemetry.io/otel/trace" 13 | ) 14 | 15 | // LogRecoverToReturn Recovers from a panic, logs and forwards it sentry and otel, then returns 16 | // Does nothing when there is no panic. 17 | func LogRecoverToReturn(ctx context.Context, loc string) { 18 | err := recover() 19 | if err == nil { 20 | return 21 | } 22 | 23 | stack := string(debug.Stack()) 24 | HandleError(ctx, loc, err, stack) 25 | } 26 | 27 | // LogRecoverToExit Recovers from a panic, logs and forwards it sentry and otel, then exits 28 | // Does nothing when there is no panic. 29 | func LogRecoverToExit(ctx context.Context, loc string) { 30 | err := recover() 31 | if err == nil { 32 | return 33 | } 34 | 35 | stack := string(debug.Stack()) 36 | HandleError(ctx, loc, err, stack) 37 | 38 | // ensure that errors still get sent out 39 | ShutdownTracer(ctx) 40 | 41 | os.Exit(1) 42 | } 43 | 44 | func HandleError(ctx context.Context, loc string, err interface{}, stack string) { 45 | msg := fmt.Sprintf("unhandled panic in %v, exiting: %v", loc, err) 46 | 47 | hub := sentry.CurrentHub() 48 | if hub != nil { 49 | hub.Recover(err) 50 | } 51 | 52 | // always log to stderr (no WithContext!) 53 | log.WithFields(log.Fields{"loc": loc, "stack": stack}).Error(msg) 54 | 55 | // if we have a context, try attaching additional info to the span 56 | if ctx != nil { 57 | log.WithContext(ctx).WithFields(log.Fields{"loc": loc, "stack": stack}).Error(msg) 58 | span := trace.SpanFromContext(ctx) 59 | span.SetAttributes(attribute.String("ovm.panic.loc", loc)) 60 | span.SetAttributes(attribute.String("ovm.panic.stack", stack)) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tracing/main_test.go: -------------------------------------------------------------------------------- 1 | package tracing 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestTracingResource(t *testing.T) { 8 | resource := tracingResource("test-component") 9 | if resource == nil { 10 | t.Error("Could not initialize tracing resource. Check the log!") 11 | } 12 | } 13 | --------------------------------------------------------------------------------