Skip to content

Commit 46270db

Browse files
authored
Merge pull request #86 from cloudgraphdev/aa-azure-capps
feat(azure): Add ContainerApp services
2 parents 8d37910 + 6a0c656 commit 46270db

File tree

16 files changed

+581
-0
lines changed

16 files changed

+581
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"terraform:cleanup": "rimraf ./tests/terraform/{.terraform,.terraform.lock.hcl,tfplan} ./tests/terraform/*.{tfstate,tfplan,backup}"
3232
},
3333
"dependencies": {
34+
"@azure/arm-appcontainers": "^2.0.0",
3435
"@azure/arm-appinsights": "^4.0.0",
3536
"@azure/arm-appservice": "^11.0.0",
3637
"@azure/arm-authorization": "^8.4.1",

src/enums/serviceMap.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import AzureCdnOrigins from '../services/cdnOrigins'
2424
import AzureCdnProfiles from '../services/cdnProfiles'
2525
import AzureCognitiveServicesAccount from '../services/cognitiveServicesAccount'
2626
import AzureContainerRegistry from '../services/containerRegistry'
27+
import AzureContainerApp from '../services/containerApp'
28+
import AzureContainerAppEnvironment from '../services/containerAppEnvironment'
2729
import AzureDataCollectionRule from '../services/dataCollectionRule'
2830
import AzureDataFactory from '../services/dataFactory'
2931
import AzureDatabaseManagedSqlInstance from '../services/databaseManagedSqlInstance'
@@ -131,6 +133,8 @@ export default {
131133
[services.cdnProfiles]: AzureCdnProfiles,
132134
[services.cognitiveServicesAccount]: AzureCognitiveServicesAccount,
133135
[services.containerRegistry]: AzureContainerRegistry,
136+
[services.containerApp]: AzureContainerApp,
137+
[services.containerAppEnvironment]: AzureContainerAppEnvironment,
134138
[services.cosmosDb]: AzureCosmosDb,
135139
[services.dataCollectionRule]: AzureDataCollectionRule,
136140
[services.dataFactory]: AzureDataFactory,

src/enums/services.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export default {
2929
cdnProfiles: 'cdnProfiles',
3030
cognitiveServicesAccount: 'cognitiveServicesAccount',
3131
containerRegistry: 'containerRegistry',
32+
containerApp: 'containerApp',
33+
containerAppEnvironment: 'containerAppEnvironment',
3234
cosmosDb: 'cosmosDb',
3335
dataCollectionRule: 'dataCollectionRule',
3436
dataFactory: 'dataFactory',

src/properties/logger.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ export default {
7070
`Found ${num} CDN origin groups`,
7171
foundContainerRegistries: (num: number): string =>
7272
`Found ${num} container registries`,
73+
foundContainerApps: (num: number): string => `Found ${num} container apps`,
74+
foundContainerAppEnvironment: (num: number): string =>
75+
`Found ${num} container environments`,
7376
/* Cosmos DB */
7477
foundCosmosDbAccounts: (num: number): string =>
7578
`Found ${num} cosmos DB accounts`,

src/services/containerApp/data.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { ContainerApp, ContainerAppsAPIClient } from '@azure/arm-appcontainers'
2+
3+
import CloudGraph from '@cloudgraph/sdk'
4+
5+
import azureLoggerText from '../../properties/logger'
6+
import { AzureServiceInput, TagMap } from '../../types'
7+
import { tryCatchWrapper } from '../../utils/index'
8+
import { regionMap } from '../../enums/regions'
9+
10+
const { logger } = CloudGraph
11+
const lt = { ...azureLoggerText }
12+
const serviceName = 'ContainerApp'
13+
14+
export interface RawAzureContainerApp
15+
extends Omit<ContainerApp, 'location' | 'tags'> {
16+
resourceGroupId?: string
17+
region: string
18+
customDomainVerificationId?: string
19+
environmentId?: string
20+
latestReadyRevisionName?: string
21+
latestRevisionFqdn?: string
22+
latestRevisionName?: string
23+
location?: string
24+
managedEnvironmentId?: string
25+
provisioningState?: string
26+
workloadProfileName?: string
27+
Tags: TagMap
28+
}
29+
30+
export default async ({
31+
config,
32+
}: AzureServiceInput): Promise<{
33+
[property: string]: RawAzureContainerApp[]
34+
}> => {
35+
try {
36+
const { tokenCredentials, subscriptionId } = config
37+
const client = new ContainerAppsAPIClient(tokenCredentials, subscriptionId)
38+
39+
const containerApps: RawAzureContainerApp[] = []
40+
const result = { global: [] }
41+
await tryCatchWrapper(
42+
async () => {
43+
for await (const containerApp of client.containerApps.listBySubscription()) {
44+
if (containerApp) {
45+
const { tags, ...rest } = containerApp
46+
47+
containerApps.push({
48+
...rest,
49+
id: rest.id.replace('/containerapps/', '/containerApps/'), // fix casing in Id
50+
region: containerApp.location || regionMap.global,
51+
Tags: tags || {},
52+
})
53+
}
54+
}
55+
},
56+
{
57+
service: serviceName,
58+
client,
59+
scope: 'containerApps',
60+
operation: 'listBySubscription',
61+
}
62+
)
63+
logger.debug(lt.foundContainerApps(containerApps.length))
64+
65+
containerApps.map(({ region, ...rest }) => {
66+
result.global.push({
67+
...rest,
68+
region,
69+
})
70+
})
71+
return result
72+
} catch (e) {
73+
logger.error(e)
74+
return {}
75+
}
76+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { RawAzureContainerApp } from './data'
2+
import { formatTagsFromMap } from '../../utils/format'
3+
import { AzureContainerApp } from '../../types/generated'
4+
5+
export default ({
6+
service,
7+
account: subscriptionId,
8+
}: {
9+
service: RawAzureContainerApp
10+
account: string
11+
}): AzureContainerApp => {
12+
const {
13+
id,
14+
name,
15+
type,
16+
region,
17+
resourceGroupId,
18+
customDomainVerificationId,
19+
environmentId,
20+
latestReadyRevisionName,
21+
latestRevisionFqdn,
22+
latestRevisionName,
23+
location,
24+
managedEnvironmentId,
25+
provisioningState,
26+
workloadProfileName,
27+
Tags = {},
28+
} = service
29+
return {
30+
id,
31+
name,
32+
type,
33+
region,
34+
resourceGroupId,
35+
customDomainVerificationId,
36+
environmentId,
37+
latestReadyRevisionName,
38+
latestRevisionFqdn,
39+
latestRevisionName,
40+
location,
41+
managedEnvironmentId,
42+
provisioningState,
43+
workloadProfileName,
44+
subscriptionId,
45+
tags: formatTagsFromMap(Tags),
46+
}
47+
}

src/services/containerApp/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Service } from '@cloudgraph/sdk'
2+
import BaseService from '../base'
3+
import format from './format'
4+
import mutation from './mutation'
5+
import getData from './data'
6+
7+
export default class AzureContainerApp extends BaseService implements Service {
8+
format = format.bind(this)
9+
10+
getData = getData.bind(this)
11+
12+
mutation = mutation
13+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default `mutation($input: [AddazureContainerAppInput!]!) {
2+
addazureContainerApp(input: $input, upsert: true) {
3+
numUids
4+
}
5+
}`
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
type azureContainerApp implements azureResource
2+
@generate(
3+
query: { get: true, query: true, aggregate: true }
4+
mutation: { add: true, delete: false }
5+
)
6+
@key(fields: "id") {
7+
location: String @search(by: [hash, regexp])
8+
provisioningState: String @search(by: [hash, regexp])
9+
managedEnvironmentId: String @search(by: [hash, regexp])
10+
environmentId: String @search(by: [hash, regexp])
11+
workloadProfileName: String @search(by: [hash, regexp])
12+
latestRevisionName: String @search(by: [hash, regexp])
13+
latestReadyRevisionName: String @search(by: [hash, regexp])
14+
latestRevisionFqdn: String @search(by: [hash, regexp])
15+
customDomainVerificationId: String @search(by: [hash, regexp])
16+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import {
2+
ContainerAppsAPIClient,
3+
ManagedEnvironment,
4+
Certificate,
5+
DaprComponent,
6+
ManagedEnvironmentStorage,
7+
} from '@azure/arm-appcontainers'
8+
9+
import CloudGraph from '@cloudgraph/sdk'
10+
import azureLoggerText from '../../properties/logger'
11+
import { AzureServiceInput, TagMap } from '../../types'
12+
import { tryCatchWrapper } from '../../utils/index'
13+
import { regionMap } from '../../enums/regions'
14+
15+
const { logger } = CloudGraph
16+
const lt = { ...azureLoggerText }
17+
const serviceName = 'ContainerAppEnvironment'
18+
19+
export interface RawAzureAppEnvironment
20+
extends Omit<ManagedEnvironment, 'location' | 'tags'> {
21+
resourceGroupId?: string
22+
certificates: Certificate[]
23+
daprComponents: DaprComponent[]
24+
storages: ManagedEnvironmentStorage[]
25+
region: string
26+
defaultDomain?: string
27+
eventStreamEndpoint?: string
28+
infrastructureResourceGroup?: string
29+
provisioningState?: string
30+
staticIp?: string
31+
subscriptionId?: string
32+
zoneRedundant?: boolean
33+
Tags: TagMap
34+
}
35+
36+
async function getDaprComponents(
37+
client: ContainerAppsAPIClient,
38+
resourceGroupName: string,
39+
resource: ManagedEnvironment
40+
): Promise<DaprComponent[]> {
41+
const daprComponents = [] as DaprComponent[]
42+
await tryCatchWrapper(
43+
async () => {
44+
for await (const daprComponent of client.daprComponents.list(
45+
resourceGroupName,
46+
resource.name
47+
)) {
48+
daprComponents.push(daprComponent)
49+
}
50+
},
51+
{
52+
service: serviceName,
53+
client,
54+
scope: 'daprComponents',
55+
operation: 'list',
56+
}
57+
)
58+
return daprComponents
59+
}
60+
61+
async function getCertificates(
62+
client: ContainerAppsAPIClient,
63+
resourceGroupName: string,
64+
resource: ManagedEnvironment
65+
): Promise<Certificate[]> {
66+
const certificates = [] as Certificate[]
67+
await tryCatchWrapper(
68+
async () => {
69+
for await (const certificate of client.certificates.list(
70+
resourceGroupName,
71+
resource.name
72+
)) {
73+
certificates.push(certificate)
74+
}
75+
},
76+
{
77+
service: serviceName,
78+
client,
79+
scope: 'certificates',
80+
operation: 'list',
81+
}
82+
)
83+
return certificates
84+
}
85+
86+
async function getStorages(
87+
client: ContainerAppsAPIClient,
88+
resourceGroupName: string,
89+
resource: ManagedEnvironment
90+
): Promise<ManagedEnvironmentStorage[]> {
91+
const storages = [] as ManagedEnvironmentStorage[]
92+
await tryCatchWrapper(
93+
async () => {
94+
const storagesCollection = await client.managedEnvironmentsStorages.list(
95+
resourceGroupName,
96+
resource.name
97+
)
98+
for (const storage of storagesCollection.value) {
99+
storages.push(storage)
100+
}
101+
},
102+
{
103+
service: serviceName,
104+
client,
105+
scope: 'managedEnvironmentsStorages',
106+
operation: 'list',
107+
}
108+
)
109+
return storages
110+
}
111+
112+
export default async ({
113+
config,
114+
}: AzureServiceInput): Promise<{
115+
[property: string]: RawAzureAppEnvironment[]
116+
}> => {
117+
try {
118+
const { tokenCredentials, subscriptionId } = config
119+
const client = new ContainerAppsAPIClient(tokenCredentials, subscriptionId)
120+
const resources: RawAzureAppEnvironment[] = []
121+
const result = { global: [] }
122+
await tryCatchWrapper(
123+
async () => {
124+
for await (const resource of client.managedEnvironments.listBySubscription()) {
125+
if (resource) {
126+
const { tags, ...rest } = resource
127+
128+
// "/subscriptions/xxx/resourceGroups/yyy/providers/Microsoft.App/containerApps/YYY"
129+
const [, , , , resourceGroupName] = resource.id.split('/')
130+
const certificates = await getCertificates(
131+
client,
132+
resourceGroupName,
133+
resource
134+
)
135+
136+
const daprComponents = await getDaprComponents(
137+
client,
138+
resourceGroupName,
139+
resource
140+
)
141+
142+
const storages = await getStorages(
143+
client,
144+
resourceGroupName,
145+
resource
146+
)
147+
148+
resources.push({
149+
...rest,
150+
region: resource.location || regionMap.global,
151+
certificates,
152+
daprComponents,
153+
storages,
154+
Tags: tags || {},
155+
})
156+
}
157+
}
158+
},
159+
{
160+
service: serviceName,
161+
client,
162+
scope: 'managedEnvironments',
163+
operation: 'listBySubscription',
164+
}
165+
)
166+
logger.debug(lt.foundContainerAppEnvironment(resources.length))
167+
168+
resources.map(({ region, ...rest }) => {
169+
result.global.push({
170+
...rest,
171+
region,
172+
})
173+
})
174+
return result
175+
} catch (e) {
176+
logger.error(e)
177+
return {}
178+
}
179+
}

0 commit comments

Comments
 (0)