I have a weird scenario here.
The following line in my Pulumi typescript code always fails the first time:
const redisCache = new gcp.redis.Instance("my-redis-cache", {
name: "my-metadata-cache",
tier: "BASIC",
memorySizeGb: 1,
authorizedNetwork: pulumi.interpolate`projects/someprojectid/global/networks/default`,
connectMode: "PRIVATE_SERVICE_ACCESS",
redisVersion: "REDIS_6_X",
displayName: "My Metadata Cache",
project: someprojectid,
}, defaultResourceOptions);
**
error: 1 error occurred:
* Error waiting to create Instance: Error waiting for Creating Instance: Error code 13, message: an internal error has occurred
**
Strangely, when I again run pulumi up, it succeeds. Has anyone else faced this before? Any clues?
Ok this turned out to be a case of working with a beast of a code. Once I started isolating the issue, things became clearer. For those who stumble across this one, here is a full working code.
import * as pulumi from "#pulumi/pulumi";
import * as gcp from "#pulumi/gcp";
export interface CacheComponentResourceArgs {
projectId : pulumi.Input<string>;
projectNumber: pulumi.Input<string>;
}
export class CacheComponentResource extends pulumi.ComponentResource {
constructor(name: string, resourceArgs: CacheComponentResourceArgs, opts?: pulumi.ResourceOptions) {
const inputs: pulumi.Inputs = {
options: opts,
};
super("ekahaa:abstracta:Cache", name, inputs, opts);
const serviceNetworkingAccessService = new gcp.projects.Service("service-nw-" + name , {
disableDependentServices: true,
project: resourceArgs.projectId,
service: "servicenetworking.googleapis.com",
}, {
parent : this
});
const redisService = new gcp.projects.Service("redis-service-" + name, {
disableDependentServices: true,
project: resourceArgs.projectId,
service: "redis.googleapis.com",
}, {
parent : this
});
const defaultGlobalAddress = new gcp.compute.GlobalAddress("default-ip-range-" + name, {
name: "default-ip-range",
purpose: "VPC_PEERING",
prefixLength: 16,
project: resourceArgs.projectId,
addressType: "INTERNAL",
network: pulumi.interpolate`projects/${resourceArgs.projectId}/global/networks/default`
}, {
parent : this,
dependsOn: [ redisService]
});
const privateServiceConnection = new gcp.servicenetworking.Connection("servicenetworking-" + name, {
service: "servicenetworking.googleapis.com",
network: pulumi.interpolate`projects/${resourceArgs.projectId}/global/networks/default`,
reservedPeeringRanges: [defaultGlobalAddress.name],
}, {
parent : this,
dependsOn: [ defaultGlobalAddress]
});
const iamBindingRedis2 = new gcp.projects.IAMBinding("iamredis2-" + name, {
members: [
pulumi.interpolate`serviceAccount:service-${resourceArgs.projectNumber}#service-networking.iam.gserviceaccount.com`
],
role: "roles/servicenetworking.serviceAgent",
project: resourceArgs.projectId
}, {
parent : this,
dependsOn: [privateServiceConnection]
});
const redisCache = new gcp.redis.Instance(name, {
name: name,
tier: "BASIC",
memorySizeGb: 1,
authorizedNetwork: pulumi.interpolate`projects/${resourceArgs.projectId}/global/networks/default`,
connectMode: "PRIVATE_SERVICE_ACCESS",
redisVersion: "REDIS_6_X",
displayName: "Abstracta Metadata Cache",
project: resourceArgs.projectId,
}, {
parent : this,
dependsOn : [redisService,serviceNetworkingAccessService,iamBindingRedis2]
});
this.registerOutputs({
redisCache : redisCache
});
}
}
let suffix = "20211018-002";
let org_name = `org-redis-demo-${suffix}`;
let projectId = `redis-demo-${suffix}` ;
const myGcpProject = new gcp.organizations.Project('ab-' + org_name, {
orgId: gcpOrgId,
projectId: projectId,
billingAccount: billingAccountId,
name: 'ab-' + org_name,
});
const myGcpProjectIAM = new gcp.projects.IAMBinding("iam-001", {
members: [
"user:vikram.vasudevan#ekahaa.com",
],
role: "roles/owner",
project: myGcpProject.projectId
});
const cacheComponentResource = new CacheComponentResource("my-cache", {
projectId : myGcpProject.projectId,
projectNumber : myGcpProject.number
}, {
dependsOn : [myGcpProjectIAM]
});
Related
I am trying to generate a credential using the didkit-wasm library with the following code, but getting error with prepareIssueCredential method: key expansion failed.
Any idea what i could be doing wrong
const did = `did:pkh:tz:` + userData.account.address;
const credential = {
'#context': [
'https://www.w3.org/2018/credentials/v1',
{
alias: 'https://schema.org/name',
description: 'https://schema.org/description',
website: 'https://schema.org/url',
logo: 'https://schema.org/logo',
BasicProfile: 'https://tzprofiles.com/BasicProfile',
},
],
id: 'urn:uuid:' + uuid(),
issuer: 'did:pkh:tz:tz1ZDSnw...',
issuanceDate: new Date().toISOString(),
type: ['VerifiableCredential', 'Company Credential'],
credentialSubject: {
id: did,
name: company.name,
description: company.description,
url: company.url,
},
};
let credentialString = JSON.stringify(credential);
const proofOptions = {
verificationMethod: did + '#TezosMethod2021', //subject's did
proofPurpose: 'assertionMethod',
};
const publicKeyJwkString = await JWKFromTezos(
'edpkuGHxcJDq9....' //issuer's public key
);
let prepStr = await prepareIssueCredential(
credentialString,
JSON.stringify(proofOptions),
publicKeyJwkString
);
I'm trying to create a public API which will be integrated with a Fargate service which already exists in private subnet.
I got below error when run cdk synthesize --profile=PandaService-Alpha.
/Users/yangliu/Projects/Panda/PandaApi/node_modules/#aws-cdk/aws-apigatewayv2-alpha/lib/http/integration.ts:249
throw new Error('Either `integrationSubtype` or `integrationUri` must be specified.');
^
Error: Either `integrationSubtype` or `integrationUri` must be specified.
at new HttpIntegration (/Users/yangliu/Projects/Panda/PandaApi/node_modules/#aws-cdk/aws-apigatewayv2-alpha/lib/http/integration.ts:249:13)
at HttpAlbIntegration._bindToRoute (/Users/yangliu/Projects/Panda/PandaApi/node_modules/#aws-cdk/aws-apigatewayv2-alpha/lib/http/integration.ts:317:26)
at new HttpRoute (/Users/yangliu/Projects/Panda/PandaApi/node_modules/#aws-cdk/aws-apigatewayv2-alpha/lib/http/route.ts:191:38)
at /Users/yangliu/Projects/Panda/PandaApi/node_modules/#aws-cdk/aws-apigatewayv2-alpha/lib/http/api.ts:458:14
at Array.map (<anonymous>)
at HttpApi.addRoutes (/Users/yangliu/Projects/Panda/PandaApi/node_modules/#aws-cdk/aws-apigatewayv2-alpha/lib/http/api.ts:455:20)
at ApigatewayStack.addApiRoutes (/Users/yangliu/Projects/Panda/PandaApi/lib/apigateway-stack.ts:110:22)
at new ApigatewayStack (/Users/yangliu/Projects/Panda/PandaApi/lib/apigateway-stack.ts:101:10)
at /Users/yangliu/Projects/Panda/PandaApi/bin/app.ts:17:3
The error is thrown in the addApiRoutes method in below code.
Code
import * as CDK from "aws-cdk-lib";
import * as CertificateManager from "aws-cdk-lib/aws-certificatemanager";
import * as Route53 from "aws-cdk-lib/aws-route53";
import * as ApiGatewayV2Alpha from "#aws-cdk/aws-apigatewayv2-alpha";
import * as ApiGatewayV2IntegrationsAlpha from "#aws-cdk/aws-apigatewayv2-integrations-alpha";
import * as ELBv2 from "aws-cdk-lib/aws-elasticloadbalancingv2";
import { Construct } from "constructs";
import { StageInfo } from "../config/stage-config";
import * as EC2 from "aws-cdk-lib/aws-ec2";
export interface ApigatewayStackProps extends CDK.StackProps {
readonly packageName: string;
readonly stageInfo: StageInfo;
}
export class ApigatewayStack extends CDK.Stack {
private readonly coreVpc: EC2.IVpc;
// Prefix for CDK constrcut ID
private readonly constructIdPrefix: string;
private readonly pandaApi: ApiGatewayV2Alpha.HttpApi;
constructor(scope: Construct, id: string, props: ApigatewayStackProps) {
super(scope, id, props);
this.coreVpc = EC2.Vpc.fromLookup(
this,
`${props.stageInfo.stageName}VpcLookupId`,
{
vpcName: "CoreVpc",
}
);
this.constructIdPrefix = `${props.packageName}-${props.stageInfo.stageName}`;
const hostedZone: Route53.IHostedZone = Route53.HostedZone.fromLookup(
this,
`${this.constructIdPrefix}-HostedZoneLookup`,
{
domainName: props.stageInfo.domainName,
}
);
const domainCertificate = new CertificateManager.Certificate(
this,
`${this.constructIdPrefix}-pandaApiCertificate`,
{
domainName: props.stageInfo.domainName,
validation:
CertificateManager.CertificateValidation.fromDns(hostedZone),
}
);
const customDomainName = new ApiGatewayV2Alpha.DomainName(
this,
`${this.constructIdPrefix}-ApiGatewayDomainName`,
{
certificate: domainCertificate,
domainName: props.stageInfo.domainName,
}
);
this.pandaApi = new ApiGatewayV2Alpha.HttpApi(
this,
`${this.constructIdPrefix}-pandaApi`,
{
defaultDomainMapping: {
domainName: customDomainName,
//mappingKey: props.pipelineStageInfo.stageName
},
corsPreflight: {
allowOrigins: ["*"],
allowHeaders: ["*"],
allowMethods: [
ApiGatewayV2Alpha.CorsHttpMethod.OPTIONS,
ApiGatewayV2Alpha.CorsHttpMethod.GET,
ApiGatewayV2Alpha.CorsHttpMethod.POST,
ApiGatewayV2Alpha.CorsHttpMethod.PUT,
],
maxAge: CDK.Duration.hours(6),
},
//createDefaultStage: false,
// only allow use custom domain
disableExecuteApiEndpoint: true
}
);
this.addApiRoutes(props);
}
/**
* Add API routes for multiple services.
*/
private addApiRoutes(props: ApigatewayStackProps) {
const PandaServiceIntegration : ApiGatewayV2IntegrationsAlpha.HttpAlbIntegration =
this.generatePandaServiceIntegration(props);
this.pandaApi.addRoutes({
path: "/products",
methods: [ApiGatewayV2Alpha.HttpMethod.ANY],
integration: PandaServiceIntegration,
});
this.pandaApi.addRoutes({
path: "/store-categories",
methods: [ApiGatewayV2Alpha.HttpMethod.ANY],
integration: PandaServiceIntegration,
});
this.pandaApi.addRoutes({
path: "/stores",
methods: [ApiGatewayV2Alpha.HttpMethod.ANY],
integration: PandaServiceIntegration,
});
}
/**
*
* #returns HttpAlbIntegration for PandaService.
*/
private generatePandaServiceIntegration(props: ApigatewayStackProps) {
const vpcLink = new ApiGatewayV2Alpha.VpcLink(
this,
`${this.constructIdPrefix}-VpcLink`,
{
vpc: this.coreVpc,
subnets: {
subnetType: EC2.SubnetType.PRIVATE_ISOLATED,
},
}
);
const PandaServiceAlbSecurityGroup = EC2.SecurityGroup.fromLookupByName(
this,
`${this.constructIdPrefix}-PandaServiceAlbSecurityGroupLookup`,
"PandaServiceAlbSecurityGroup",
this.coreVpc
);
const PandaServiceAlbListener : ELBv2.IApplicationListener =
ELBv2.ApplicationListener.fromApplicationListenerAttributes(this, `${this.constructIdPrefix}-PandaServiceAlbListenerLookUp`, {
listenerArn: props.stageInfo.PandaServiceAlbArn,
securityGroup: PandaServiceAlbSecurityGroup,
});
const PandaServiceIntegration: ApiGatewayV2IntegrationsAlpha.HttpAlbIntegration =
new ApiGatewayV2IntegrationsAlpha.HttpAlbIntegration(
`${this.constructIdPrefix}-PandaServiceIntegration`,
PandaServiceAlbListener ,
{
method: ApiGatewayV2Alpha.HttpMethod.ANY,
vpcLink: vpcLink,
secureServerName: props.stageInfo.domainName,
parameterMapping: new ApiGatewayV2Alpha.ParameterMapping()
}
);
return PandaServiceIntegration;
}
}
As Otavio pointed out, my props.stageInfo.PandaServiceAlbArn is an empty string, after updating it with the actual string the problem get resolved.
This error occasionally occurs on "cdk watch" and disappears when I destroy and redeploy the stack. All the global and per lambda variables are strings for sure. The table name is not declared explicitly but generated from the id..(maybe this is the cause of the issue?)
export class MyStack extends Stack {
constructor(app: App, id: string, props: MyStackProps) {
super(app, id);
const isProd = props.deploymentEnv;
const stackName = Stack.of(this).stackName;
const PRIMARY_KEY = 'reportId';
const dynamoTable = new Table(this, `MyTable-${stackName}`, {
partitionKey: {
name: PRIMARY_KEY,
type: AttributeType.STRING,
},
stream: StreamViewType.NEW_IMAGE,
removalPolicy: isProd ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY,
});
// Default props for lambda functions
const nodeJsFunctionProps: NodejsFunctionProps = {
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
'#sparticuz/chrome-aws-lambda',
],
},
depsLockFilePath: join(__dirname, '../package-lock.json'),
environment: {
PRIMARY_KEY: PRIMARY_KEY,
TABLE_NAME: dynamoTable.tableName,
},
runtime: Runtime.NODEJS_16_X,
};
In the lambda file, I'm getting the variables this way:
const TABLE_NAME = process.env.TABLE_NAME ?? '';
The error:
failed: InvalidParameterType: Expected params.Environment.Variables['TABLE_NAME'] to
be a string
I see instructions at https://www.npmjs.com/package/#google-cloud/logging to start logging, but I can't seem to see the logs in https://console.cloud.google.com/run/detail/<location>/<service>/logs?project=<project> for CloudRun, and I'm not sure where they are (I invoked them with quickstart('my-project', 'my-log').
To find logs the logs from the tutorial, use the logs explorer https://console.cloud.google.com/logs/query with the query logName="projects/<project>/logs/<log_name>", so in your case, logName="projects/my-project/logs/my-log" of when you're editing the query you can find it in the dropdown under log name.
To log to CloudRun you need to set the following in the meta data:
{
...,
resource: {
type: 'cloud_run_revision',
labels: { service_name: 'my-service', location: 'us-east1' } },
}
You can use this sample function to test:
const log = async (text, name) => {
const gcpLogger = new Logging({ projectId: 'smodin-dev' })
const logSet = gcpLogger.log(name)
const metadata = {
severity: 'INFO',
resource: {
type: 'cloud_run_revision',
labels: { service_name: 'my-service', location: 'us-east1' }
},
}
const entry = logSet.entry(metadata, text)
await logSet.write(entry)
}
PS: If there is a different recommended method I'm all ears
I have VPC peering to connect to a lambda in one aws account to a RDS instance in another aws account. This works fine but required the VPC peering to have DNS resolution option enabled.
By default DNS resolution is set to :
DNS resolution from accepter VPC to private IP :Disabled.
This can be done via the AWS console and the CLI. I am not able to achieve the same using AWS CDK.
https://docs.aws.amazon.com/vpc/latest/peering/modify-peering-connections.html
The CfnVPCPeeringConnection does not seem to have this option.
https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-ec2.CfnVPCPeeringConnection.html
Is there any other way of achieving this via CDK ?
const cfnVPCPeeringConnection :CfnVPCPeeringConnection =
new CfnVPCPeeringConnection(
stack,
"vpcPeeringId",
{
peerVpcId : "<vpcId of acceptor account>",
vpcId : "<reference of the Id>",
peerOwnerId : "<aws acc number>",
peerRegion : "<region>",
peerRoleArn :"<arn created in the acceptor account>"",
}
);
//update route tables
rdsConnectorVpc.isolatedSubnets.forEach(({ routeTable: { routeTableId } }, index) => {
new CfnRoute(this.parentStack, 'PrivateSubnetPeeringConnectionRoute' + index, {
destinationCidrBlock: '<CIDR>',
routeTableId,
vpcPeeringConnectionId: cfnVPCPeeringConnection.ref,
})
});
You can use a CustomResource Construct in AWS CDK to achieve it:
import * as cdk from "#aws-cdk/core";
import ec2 = require("#aws-cdk/aws-ec2");
import iam = require("#aws-cdk/aws-iam");
import { AwsCustomResource, AwsCustomResourcePolicy, AwsSdkCall, PhysicalResourceId } from "#aws-cdk/custom-resources";
import { RetentionDays } from "#aws-cdk/aws-logs";
export interface AllowVPCPeeringDNSResolutionProps {
vpcPeering: ec2.CfnVPCPeeringConnection,
}
export class AllowVPCPeeringDNSResolution extends cdk.Construct {
constructor(scope: cdk.Construct, id: string, props: AllowVPCPeeringDNSResolutionProps) {
super(scope, id);
const onCreate:AwsSdkCall = {
service: "EC2",
action: "modifyVpcPeeringConnectionOptions",
parameters: {
VpcPeeringConnectionId: props.vpcPeering.ref,
AccepterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: true,
},
RequesterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: true
}
},
physicalResourceId: PhysicalResourceId.of(`allowVPCPeeringDNSResolution:${props.vpcPeering.ref}`)
};
const onUpdate = onCreate;
const onDelete:AwsSdkCall = {
service: "EC2",
action: "modifyVpcPeeringConnectionOptions",
parameters: {
VpcPeeringConnectionId: props.vpcPeering.ref,
AccepterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: false,
},
RequesterPeeringConnectionOptions: {
AllowDnsResolutionFromRemoteVpc: false
}
},
};
const customResource = new AwsCustomResource(this, "allow-peering-dns-resolution", {
policy: AwsCustomResourcePolicy.fromStatements([
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: ["*"],
actions: [
"ec2:ModifyVpcPeeringConnectionOptions",
]
}),
]),
logRetention: RetentionDays.ONE_DAY,
onCreate,
onUpdate,
onDelete,
});
customResource.node.addDependency(props.vpcPeering);
}
}
and use it like this:
[...]
const peerConnection = new ec2.CfnVPCPeeringConnection(this, "peerConnection", {
vpcId: destinationVPC.vpcId,
peerVpcId: lambdaVPCToDestinationVPC.vpcId,
});
new AllowVPCPeeringDNSResolution(this, "peerConnectionDNSResolution", {
vpcPeering: peerConnection,
});
[...]