Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GCP CLI new, list and remove packet mirroring #112

Merged
merged 6 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metlo/cli",
"version": "0.0.7",
"version": "0.0.8",
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
200 changes: 200 additions & 0 deletions cli/src/gcp/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import AsyncRetry from "async-retry"
import { v4 as uuidv4, validate } from "uuid"
import fs from "fs"
import { prompt } from "enquirer"
import { GCP_REGIONS_SUPPORTED, wait_for_global_operation, wait_for_regional_operation, wait_for_zonal_operation } from "./gcpUtils"
import { GCP_CONN } from "./gcp_apis"
import chalk from "chalk"
import ora from "ora";
import { google } from "@google-cloud/compute/build/protos/protos"

const spinner = ora()

const verifyAccountDetails = async () => {
const gcp_regions = GCP_REGIONS_SUPPORTED.map(e => ({
name: e,
}))
const resp = await prompt([
{
type: "input",
name: "_projectName",
message: "GCP Project Name",
}, {
type: "input",
initial: "default",
name: "_networkName",
message: "GCP Network to mirror",
}, {
type: "autocomplete",
name: "_zoneName",
message: "Select your GCP zone",
initial: 1,
choices: gcp_regions,
}, {
type: "input",
name: "_keyPath",
message: "Path to GCP key file",
validate: (path: string) => {
if (fs.existsSync(path)) {
return true
} else {
// @ts-ignore
let text = chalk.redBright(`GCP Key file not found at ${path}`)
return text
}
}
}
])

spinner.text = "Validating account details"
spinner.start()
// @ts-ignore Destructuring is improperly done
const { _projectName: project, _networkName: network, _zoneName: zone, _keyPath: keyFilePath } = resp;

const key = (fs.readFileSync(keyFilePath)).toString("utf-8");

let conn = new GCP_CONN(key, zone, project)
await conn.test_connection()
await conn.get_zone({ zone })
spinner.succeed("Validated account details")
spinner.stop()
spinner.clear()
return { project, network, zone, key }
}


const deletePacketMirroringResources = async (
conn: GCP_CONN,
mirroring: google.cloud.compute.v1.IPacketMirroring[]
) => {
if (mirroring.length == 0) {
throw new Error("No existing packet mirroring instances found")
}

const instanceChoices = mirroring.flatMap(
(mirror) => mirror.mirroredResources.instances.map(
inst => {
const splits = inst.url.split("/");
return splits[splits.length - 1]
}
)
)
const subnetChoices = mirroring.flatMap(
(mirror) => mirror.mirroredResources.subnetworks.map(
inst => {
const splits = inst.url.split("/");
return splits[splits.length - 1]
}
)
)
const tagChoices = mirroring.flatMap(
(mirror) => mirror.mirroredResources.tags
)

const availableChoices = []
if (instanceChoices.length > 0) {
availableChoices.push("INSTANCE")
}
if (subnetChoices.length > 0) {
availableChoices.push("SUBNET")
}
if (tagChoices.length > 0) {
availableChoices.push("TAG")
}

const sourceTypeResp = await prompt([
{
type: "autocomplete",
name: "_packetMirrorName",
message: "Select Packet Mirroring instance",
initial: 0,
choices: mirroring.filter((inst) => inst.name.startsWith("metlo")).map((inst) => inst.name)
}, {
type: "select",
name: "_sourceType",
message: "Select your mirror source type",
initial: 0,
choices: availableChoices,
},
])
let sourceType = sourceTypeResp["_sourceType"]
let packetMirrorName = sourceTypeResp["_packetMirrorName"]

if (sourceType === "INSTANCE") {
const instanceNameResp = await prompt([
{
type: "autocomplete",
name: "_name",
message: "Enter the mirror source instance name to remove",
choices: instanceChoices

}
])
spinner.start("Verifying mirror source details")
const instanceName = instanceNameResp['_name'].trim()

const resources = mirroring.find((mirror) => mirror.name === packetMirrorName).mirroredResources
resources.instances = resources.instances.filter((inst) => !inst.url.includes(instanceName))
resources.instances = resources.instances.length > 0 ? resources.instances : null

let resp = await conn.remove_packet_mirroring_resources({ packetMirrorName, newMirroredResources: resources })
} else if (sourceType === "SUBNET") {
const subnetNameResp = await prompt([
{
type: "autocomplete",
name: "_name",
message: "Enter the mirror source subnet name to remove",
choices: subnetChoices
}
])
spinner.start("Verifying mirror source details")
const subnetName = subnetNameResp['_name'].trim()

const resources = mirroring.find((mirror) => mirror.name === packetMirrorName).mirroredResources
resources.subnetworks = resources.subnetworks.filter((inst) => !inst.url.includes(subnetName))
resources.subnetworks = resources.subnetworks.length > 0 ? resources.subnetworks : null

let resp = await conn.remove_packet_mirroring_resources({ packetMirrorName, newMirroredResources: resources })
} else if (sourceType === "TAG") {
const tagNameResp = await prompt([
{
type: "autocomplete",
name: "_name",
message: "Enter the mirror source tag name to remove",
choices: tagChoices
}
])
spinner.start("Verifying mirror source details")
let tagName = tagNameResp["_name"].trim()

const resources = mirroring.find((mirror) => mirror.name === packetMirrorName).mirroredResources
resources.tags = resources.tags.filter((inst) => !inst.includes(tagName))

let resp = await conn.remove_packet_mirroring_resources({ packetMirrorName, newMirroredResources: resources })
}
spinner.succeed("Deleted resource from packet mirroring Succesfully")
spinner.stop()
spinner.clear()
return {}
}

export const gcpTrafficMirrorDelete = async () => {
const id = uuidv4()
const data = {}
try {
const { project, zone, network, key } = await verifyAccountDetails()
const networkUrl = `https://www.googleapis.com/compute/v1/projects/${project}/global/networks/${network}`
const conn = new GCP_CONN(key, zone, project);
data["zone"] = zone
data["project"] = project

const [packetMirrors] = await conn.list_packet_mirroring()

await deletePacketMirroringResources(conn, packetMirrors)
} catch (e) {
spinner.fail()
console.log(chalk.bgRedBright("Metlo packet mirroring item removal failed. This might help in debugging it."))
console.log(e)
console.log(data)
}
}
88 changes: 87 additions & 1 deletion cli/src/gcp/gcp_apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
ZoneOperationsClient,
MachineTypesClient,
} from "@google-cloud/compute"
import { google } from "@google-cloud/compute/build/protos/protos"
import { wait_for_regional_operation } from "./gcpUtils"

const PREFIX_LENGTH = 24
const METLO_DATA_COLLECTOR_TAG = "metlo-capture"
Expand Down Expand Up @@ -209,7 +211,8 @@ export class GCP_CONN {
},
allowed: [
{
IPProtocol: "all",
IPProtocol: "UDP",
ports: ["4789"]
},
],
},
Expand Down Expand Up @@ -523,4 +526,87 @@ export class GCP_CONN {
packetMirroring: packetMirroringURL,
})
}

public async list_packet_mirroring() {
let conn = new PacketMirroringsClient({ credentials: this.keyfile })
return conn.list({
project: this.project,
region: this.region
})
}

public async get_packet_mirroring({ packetMirrorName }) {
let conn = new PacketMirroringsClient({ credentials: this.keyfile })
return conn.get({
project: this.project,
region: this.region,
packetMirroring: packetMirrorName
})
}

public async update_packet_mirroring({ packetMirrorName, updateInstance, updateTag, updateSubnet }: updatePacketMirroringInterface) {
let conn = new PacketMirroringsClient({ credentials: this.keyfile })
let [mirrorInfo, ,] = await this.get_packet_mirroring({ packetMirrorName })
let updatedMirrorInfo: google.cloud.compute.v1.IPacketMirroring = {
...mirrorInfo, mirroredResources: {
tags: updateTag ? [updateTag, ...mirrorInfo.mirroredResources.tags] : mirrorInfo.mirroredResources.tags,
instances: updateInstance ? [updateInstance, ...mirrorInfo.mirroredResources.instances] : mirrorInfo.mirroredResources.instances,
subnetworks: updateSubnet ? [updateSubnet, ...mirrorInfo.mirroredResources.subnetworks] : mirrorInfo.mirroredResources.subnetworks,
}
}
return conn.patch({
project: this.project,
region: this.region,
packetMirroring: packetMirrorName,
packetMirroringResource: updatedMirrorInfo
})
}

public async remove_packet_mirroring_resources({ packetMirrorName, newMirroredResources }: deletePacketMirroringResourcesInterface) {
let conn = new PacketMirroringsClient({ credentials: this.keyfile })
let [mirrorInfo, ,] = await this.get_packet_mirroring({ packetMirrorName })

const [delOpRegional, ,] = await conn.delete({ project: this.project, region: this.region, packetMirroring: packetMirrorName })
await wait_for_regional_operation(delOpRegional.latestResponse.name, this)
if (
newMirroredResources.instances.length == 0 &&
newMirroredResources.subnetworks.length == 0 &&
newMirroredResources.tags.length == 0
) {
const [createOpRegional, ,] = await this.start_packet_mirroring({
name: mirrorInfo.name,
networkURL: mirrorInfo.network.url,
mirroredInstanceURLs: [],
mirroredSubnetURLS: [],
mirroredTagURLs: ["metlo-provided-placeholder-network-tag"],
loadBalancerURL: mirrorInfo.collectorIlb.url
})
await wait_for_regional_operation(createOpRegional.latestResponse.name, this)
} else {
const [createOpRegional, ,] = await this.start_packet_mirroring({
name: mirrorInfo.name,
networkURL: mirrorInfo.network.url,
mirroredInstanceURLs: newMirroredResources.instances.map((inst) => inst.url),
mirroredSubnetURLS: newMirroredResources.subnetworks.map((inst) => inst.url),
mirroredTagURLs: newMirroredResources.tags,
loadBalancerURL: mirrorInfo.collectorIlb.url
})
await wait_for_regional_operation(createOpRegional.latestResponse.name, this)
}

return
}

}

interface updatePacketMirroringInterface {
packetMirrorName: string;
updateTag?: string;
updateInstance?: google.cloud.compute.v1.IPacketMirroringMirroredResourceInfoInstanceInfo;
updateSubnet?: google.cloud.compute.v1.IPacketMirroringMirroredResourceInfoSubnetInfo;
}

interface deletePacketMirroringResourcesInterface {
packetMirrorName: string
newMirroredResources: google.cloud.compute.v1.IPacketMirroringMirroredResourceInfo
}
Loading