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

feat(codebuild): add support for local cache modes #2529

Merged
merged 7 commits into from
May 17, 2019

Conversation

SanderKnape
Copy link

@SanderKnape SanderKnape commented May 12, 2019

Fixes #1956


Pull Request Checklist

  • Testing
    • Unit test added (prefer not to modify an existing test, otherwise, it's probably a breaking change)
    • CLI change?: coordinate update of integration tests with team
    • cdk-init template change?: coordinated update of integration tests with team
  • Docs
    • jsdocs: All public APIs documented
    • README: README and/or documentation topic updated
    • Design: For significant features, design document added to design folder
  • Title and Description
    • Change type: title prefixed with fix, feat and module name in parens, which will appear in changelog
    • Title: use lower-case and doesn't end with a period
    • Breaking?: last paragraph: "BREAKING CHANGE: <describe what changed + link for details>"
    • Issues: Indicate issues fixed via: "Fixes #xxx" or "Closes #xxx"
  • Sensitive Modules (requires 2 PR approvers)
    • IAM Policy Document (in @aws-cdk/aws-iam)
    • EC2 Security Groups and ACLs (in @aws-cdk/aws-ec2)
    • Grant APIs (only if not based on official documentation with a reference)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license.


BREAKING CHANGE: the CodeBuild Project cacheBucket and cacheDir properties are removed in favour of the new cache property that allows no caching (default), local caching (new) and S3 caching

Copy link
Contributor

@eladb eladb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution�!

Please update README with some details on this feature and use-cases (copy & paste from AWS docs)

@@ -16,6 +16,12 @@ const CODEPIPELINE_TYPE = 'CODEPIPELINE';
const S3_BUCKET_ENV = 'SCRIPT_S3_BUCKET';
const S3_KEY_ENV = 'SCRIPT_S3_KEY';

export enum ProjectCacheModes {
LocalSourceCache = 'LOCAL_SOURCE_CACHE',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prefix "Local" seems unnecessary

@@ -16,6 +16,12 @@ const CODEPIPELINE_TYPE = 'CODEPIPELINE';
const S3_BUCKET_ENV = 'SCRIPT_S3_BUCKET';
const S3_KEY_ENV = 'SCRIPT_S3_KEY';

export enum ProjectCacheModes {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add docstrings


### Local Caching

With local caching, the cache is stored on the codebuild instance itself. Three different cache modes are supported:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that's not clear from this is what causes a reuse of an instance? When is the local cache being utilized.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why this we shouldn't enable this by default?

@skinny85

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also isn't entirely clear to me to be honest. The only hint regarding this is that "After a build, AWS CodeBuild maintains the cache for some time in anticipation of another build." (https://aws.amazon.com/about-aws/whats-new/2019/02/aws-codebuild-now-supports-local-caching/). My documentation is largely copy/pasted from the official documentation where this also isn't clearly mentioned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kaixiang-AWS can you weigh in here? Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeBuild cannot guarantee a reuse of instance. It's a best effort. Imagine you have a build ran and cached on an instance, and then you have 2 builds start at the same time. Only one of them will utilize the cache and the other one will have to run on a new instance which doesn't have cache in it.
Anyway, I think the problem here is we might miss a modeling opportunity if we do it this way. Defining a base cache class and extending it to S3 or local caching might be a more feasible and extensible way. If there are more cache types in the future, in this way we only need to add another class which extends base class and don't need to handle the complicated input validation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Can we put this info into the README?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! I also looked into the refactor that @Kaixiang-AWS mentioned. I'm currently not experienced enough with Typescript and/or the CDK to properly implement this. Can we for now merge this PR and create a new issue to refactor it? The feature would at least become available if we merge the PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like @Kaixiang-AWS's suggestion. It's a pattern we've been using we call "union-like classes" and it fits this API perfectly. I rather we spend a bit more time now than introduce a breaking change later.

Here's a sketch:

export interface BucketCacheOptions {
  cacheDir?: string;
}

export enum LocalCacheMode {
  Source = 'LOCAL_SOURCE_CACHE',
	DockerLayer = 'LOCAL_DOCKER_LAYER_CACHE',
	Custom = 'LOCAL_CUSTOM_CACHE',
}

export abstract class Cache {
  public static none(): Cache {
    return { _toCloudFormation: () => undefined };
  }

  public static bucket(bucket: s3.IBucket, options: BucketCacheOptions): Cache {
    const cacheDir = options.cacheDir ? options.cacheDir : Aws.noValue;
    return { _toCloudFormation: () => ({
      type: 'S3',
      location: Fn.join('/', [ bucket.bucketName, cacheDir ])})
    };
  }

  public static local(...modes: LocalCacheMode[]): Cache {
    return {
      _toCloudFormation: () => ({
        type: 'LOCAL',
        modes
      })
    };
  }

  /**  @internal */
  public abstract _toCloudFormation(): codebuild.CfnProject.ProjectCacheProperty | undefined;
}

export interface CommonProjectProps {
  /**
   * @default Cache.none
   */
  readonly cache?: Cache;
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I'll dive into it 👍

@SanderKnape
Copy link
Author

Just pushed an update. Small notes:

  • Instead of the BucketCacheOptions from @eladb his example, I simply use a prefix string property. This is the same terminology as used in documentation/cloudformation. And I don't see a reason for adding that property to an object - but let me know if that is desired.
  • I implemented a _bind() function to allow the S3 caching strategy to grant permissions for the CodeBuild Project to add files to S3

With S3 caching, the cache is stored in an S3 bucket which is available from multiple hosts.

```typescript
new codebuild.Project(stack, 'Project', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace stack with this everywhere


With local caching, the cache is stored on the codebuild instance itself. CodeBuild cannot guarantee a reuse of instance. For example, when a build starts and caches files locally, if two subsequent builds start at the same time afterwards only one of those builds would get the cache. Three different cache modes are supported:

* `LocalCacheMode.SourceCache` caches Git metadata for primary and secondary sources.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the postfix Cache from these enum members.

* Local cache modes to enable for the CodeBuild Project
*/
export enum LocalCacheMode {
SourceCache = 'LOCAL_SOURCE_CACHE',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 spaces please

packages/@aws-cdk/aws-codebuild/lib/cache.ts Show resolved Hide resolved
/**
* If and what strategy to use for build caching
*/
export enum CacheType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to export this ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why it's needed really.

packages/@aws-cdk/aws-codebuild/lib/cache.ts Show resolved Hide resolved
}),
_bind: (project) => {
if (project.role) {
bucket.grantReadWrite(project.role);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Project should implement IGrantable and then you should be able to just do bucket.grantReadWrite(project).

* @param bucket the S3 bucket to use for caching
* @param prefix the optional prefix to use to store the cache in the bucket
*/
public static bucket(bucket: IBucket, prefix?: string): Cache {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but please use the the Options API because it is future-compatible. If we end up needing more options, we'll have to break this API. It's also more readable when it's used: Cache.bucket(myBucket, 'foo') as oppose to Cache.bucket(myBucket, { prefix: 'foo' }).

@@ -696,7 +686,7 @@ export class Project extends ProjectBase {
environment: this.renderEnvironment(props.environment, environmentVariables),
encryptionKey: props.encryptionKey && props.encryptionKey.keyArn,
badgeEnabled: props.badge,
cache,
cache: props.cache && props.cache._toCloudFormation() || Cache.none()._toCloudFormation(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: call ._toCloudFormation once, so usually we do this:

const cache = props.cache || Cache.none();

// ...

  cache: cache._toCloudFormation();

@SanderKnape SanderKnape force-pushed the sanderknape/codebuild-cache-modes branch from f167746 to c49fdc7 Compare May 14, 2019 15:34
@SanderKnape SanderKnape force-pushed the sanderknape/codebuild-cache-modes branch 3 times, most recently from 3d16852 to 153d4db Compare May 14, 2019 15:55
@SanderKnape SanderKnape force-pushed the sanderknape/codebuild-cache-modes branch from 153d4db to f2fc1be Compare May 14, 2019 19:46
```typescript
new codebuild.Project(this, 'Project', {
source: new codebuild.CodePipelineSource(),
cache: Cache.bucket(new Bucket(this, 'Bucket'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be codebuild.Cache

```typescript
new codebuild.Project(this, 'Project', {
source: new codebuild.CodePipelineSource(),
cache: Cache.local(LocalCacheMode.DockerLayer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Show an example with two modes to demonstrate that this is a list

@SanderKnape
Copy link
Author

Hi @eladb , I've incorporated all your changes - including the last ones about the docs. Can you take another peek?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CodeBuild: Support LOCAL_DOCKER_LAYER_CACHE
4 participants