Quick Project: KubeconVibes
Tags: aws
Published: 2020-08-27 21:16:35

While attending [Kubecon EU 2020](https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/?), I wanted to maximize my time by watching the most popular sessions. Which hopefully would also be the most useful. When the sessions go up on YouTube, I can look at the view counts to judge, but right now there didn't appear to be anything on the website that provides this information. Time to look behind the curtains!

Turned out they have a nice little API running that you can access utilizing your session cookie. They return the sessions based on the tracks, so I was able to note down the URLs for each track:

* 101 Track
* Application + Development
* Case Studies
* CI/CD
* Community
* Customizing + Extended Kubernetes
* Experiences
* Keynotes
* Lightning Talks
* Machine Learning + Data
* Maintainer Track
* Networking
* Observability
* Operations
* Performance
* Runtimes
* Security + Identity + Policy
* Serverless
* Service Mesh
* Storage
* Tutorials

Hitting one of these tracks returned a ton of information on each session including the number of likes and views! BINGO! 

The only authentication was utilizing my session cookie, but since it was only a quick project, I decided to pass that parameter in during the CI/CD deploy rather than coding up a request... I didn't want to inadvertently checkin my password during testing...

```
  let kubeConTracks = [
    {name: '101 Track', url: 'https://onlinexperiences.com/scripts/Server.nxp?LASCmd=AI:1;F:LBSEXPORT!JSON&SQLID=14523&EventPackageKey=55064&RandomValue=1597773745984'},
    ...
  ]

  let options = {
    json: true,
    headers: {
      'Cookie': kubeconCookie
    }
  };

  for(let track of kubeConTracks) {
    request(track.url, options, (error, res, body) => {
      let sessionsArray = body.ResultSet[0];
      for (let session of sessionsArray) {
        console.log(session.NumViews.toString());
      }
    }
  }
```

Using the AWS CDK I was able to quickly turn this into a Cron Job to pull the information and store it in a DynamoDB table. 

```
    const kubeconCookie = this.node.tryGetContext('kubecon_cookie');

    const consumeFunction = new lambda.Function(this, 'KubeConConsume', {
      runtime: lambda.Runtime.NODEJS_10_X,
      handler: 'index.handler',
      code: lambda.Code.asset('./resources/consumer'),
      description: 'Query KubeCon APIs to get the latest data and insert into DynamoDB',
      timeout: core.Duration.seconds(30),
      environment: {
        DYNAMODB_TABLE: table.tableName,
        DYNAMODB_HISTORY_TABLE: historyTable.tableName,
        KUBECON_COOKIE: kubeconCookie
      },
    });

    const lambdaTarget = new eventstargets.LambdaFunction(consumeFunction);

    new events.Rule(this, 'ScheduleRule', {
      schedule: events.Schedule.cron({ minute: '0' }),
      targets: [lambdaTarget],
    });
```

I setup two tables, one to contain the latest information and another to group it by time - with an idea of maybe seeing how the popularity of sessions changed over the days.

With the data stored, the only thing remaining was slapping a quick API onto my DynamoDB table and throw up a front end website! I was able to structure everything in the single repo which I liked, seperating the lambdas, the front end and the infrastructure pulling everything together.

The AWS CDK has made it a lot easier to setup an API Gateway, certainly compared to building this up in straight Cloudformation.

```
    const listFunction = new lambda.Function(this, 'KubeConList', {
      runtime: lambda.Runtime.NODEJS_10_X,
      handler: 'index.list',
      code: lambda.Code.asset('./resources/api'),
      description: 'Used by the API Gateway to return the data.',
      environment: {
        DYNAMODB_TABLE: table.tableName
      },
    });

    table.grantReadData(listFunction);

    const api = new apigateway.RestApi(this, "kubeconvides-api", {
      restApiName: "KubeCon Vibes",
      description: "This service to access the kubecon data.",
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS
      }
    });

    const getIntegration = new apigateway.LambdaIntegration(listFunction, {
      requestTemplates: { "application/json": '{ "statusCode": "200" }' }
    });

    api.root.addMethod("GET", getIntegration);
```

Actually adding a custom domain to the API Gateway is more complicated than setting up the endpoint itself!
```
    const domainName = 'kubeconvibes.com'

    const zone = route53.HostedZone.fromLookup(this, 'KubeConZone', {
      domainName: domainName
    });

    const cert = new acm.Certificate(this, 'Certificate', {
      domainName: 'api.' + domainName,
      validation: acm.CertificateValidation.fromDns(zone)
    });

    const customDomain = new apigateway.DomainName(this, 'customDomain', {
      domainName: 'api.' + domainName,
      certificate: cert,
      endpointType: apigateway.EndpointType.EDGE
    });

    new apigateway.BasePathMapping(this, 'CustomBasePathMapping', {
      domainName: customDomain,
      restApi: api
    });

    new route53.CnameRecord(this, 'ApiGatewayRecordSet', {
      zone: zone,
      recordName: 'api',
      domainName: customDomain.domainNameAliasDomainName
    });
```

In short, I enjoy throwing out quick projects - especially when I want to extend my understanding of some functionality. While none of this had anything to do with Kubernetes, I did learn more about CDK and React!

The code for this project is available here: [kubecon-eu-popular-sessions](https://github.com/johncolmdoyle/kubecon-eu-popular-sessions/)

The website is: [KubeconVibes](https://kubeconvibes.com)