[MUSIC]
>> Hey, everybody.
Did a little bit more integrating with Azure DevOps,
and there was one thing I discovered,
you can actually set up build definitions in Terraform.
The first thing that you do is that you create your repository.
For example, my repository is going to be API.
What you'll do after that is you actually set
up a branch policy validation.
If we go to Azure Pipelines,
you'll see that before I have a bunch of triggers,
the thing about doing it this way is though,
it doesn't necessarily tell Azure DevOps,
"Hey, what PRs do you want this attached to?"
What I would like is for PRs to trigger
based off of branch policies for everything else.
If we go back to Terraform at our highest level,
I'm going to first initialize my repository,
and then right after that,
I'm actually going to set up my build definition.
I want it to be a CI trigger and I want to use the yaml and then I
also want it to be attached to this repository, the API.
This will generate this right here,
but this isn't enough.
What you need to do is you need to create
a branch policy build validation.
This is essentially how you tell
your build definition associate with PRs.
Realistically, I only want to make changes
and run pipelines when I have a open PR.
You can either go to the branches themselves and
do a branch policy right here.
Here's my policy right here.
Successful build required.
It's for the API build pipeline,
the build definition automatic,
and then build expiration 12 hours if main has been updated.
Now when we go back to
the repository and then I go to my merge requests,
we'll now see a successful build required.
Successful build required succeeded.
Now I have the ability of actually attaching my build pipelines
to my PRs and making sure that I'm only
building when I'm doing PRs and things like that.
Because I want to have
native support for hadolint in my pipelines,
I actually am using hadolint to lint my hadolint talker vile.
I'm building my own task image
and then I'm going to reuse that for all of my other services.
One of the other reasons why I'm creating
my own task runners is because hadolint.yaml,
this is a configuration file that you can
tune to whatever you do and don't want.
It basically requires you to go in and do
this for every package that you install.
I don't want to do that, so I just went into
the hadolint config file that I'm using and I
just put ignored DL3018,
so now I won't get that error in my pipelines.
One other thing, I integrated coverage and testing.
If you do go to Azure Pipelines,
boom, run unit tests,
I wanted to be able to have all of
this test integration and all that stuff in Azure Pipelines too.
If you want to have some type of output,
you need to at least make sure
that the reporters is set to default,
and then we've got our new one here,
which is reporters jest junit.
You got to make sure you've got both of
these if you want the command line output,
if I do LL,
we should see there is a junit.xml,
and if I hit "Edit" on that,
you can see actually there's all my test use cases,
their times, all that good stuff with it.
Now that I've got that, it'll say, "Hey,
if you want to publish these tests results, well then awesome.
We've got a sweet little task already made for you."
Then I'm saying, "Hey, on condition of succeeded or failed,
take the JUnit test runner and then get
any test files that match essentially whatever mine outputs as."
If I want this to work,
I need to make sure that right here I've got junit.xml.
We also have code coverage.
That's a very common thing as well.
Code coverage is basically supported by either
using the Cobertura or JaCoCo.
I now have a singular cobertura-coverage.xml file.
This XML file is the other half.
You literally publish that up
using the published code coverage results.
What this means is essentially if we
go to Validate and then I click
on "Publish Code Coverage," you'll see
generated coverage HTML report at blah, blah.
This is optional, but you will see
this show up as an error potentially
there's a warning in your pipelines.
If you have code coverage that already generates HTML,
you don't want to use that.
Their suggested approach of installing .NET is by using
the task because you'll see here
these agents have a tools directory.
If you've ever had to install .NET before,
you will appreciate that you can do this.
Once you have the .NET Core
installed and you're publishing your code results,
you should see at least a command run right here,
and then you should see this report generation took 0.6
seconds and this test page will show you your unit tests.
But if I click on this,
we'll then see our code coverage.
Now that I've got all of that setup in my pipelines,
we need to run migrations
in these pipelines via containers in the Cloud.
We have pipeline on
merge and we want to trigger at least one container.
This is the API container with the entry point MIGRATE.
This is what needs to be ran in
the Cloud and migrate our database.
This container needs to be prepackaged and
runnable basically before we're even able to do anything.
Let's go to ormconfig.
It's looking for something on localhost
on port 32395. Here let's do this.
Go to Docker Postgres because I'm using PostgresSQL here.
Lets just grab this, copy and paste this command.
I'm going to RM because I don't need this afterwards.
Postgres password will just keep as my secret password.
Now, I need to port forward 32395 to the default Postgres port.
I'm going to essentially start this container.
You can see my container's running now.
Five seconds, all that good stuff.
Here we go. Let's see.
That works first-time. It worked.
Again, this isn't the actual database,
but we're making sure that our migrations work.
We are not just testing our code,
but we're also testing our migrations.
But then when we actually merge to main,
that's when we're going to run it in the Cloud.
Right now, what we're going to do
first is we're just going to get our migrations
running in CI so that we
know that our migrations are working, and then later on,
once we have that running and we have
that reproducible image that we can
easily just give variables to and run,
then we'll make it so that we can essentially run it in
the Cloud against the actual PostgresSQL database.
See this right here, resources,
we're going to grab that.
Instead of this being builder,
we're going to call this Postgresql.
We could pull down this Postgres image and then push
it up to ours so that when we use it here,
we know that we're pulling it from a safe place.
We know that it's not going to change from under us.
I'm pulling it down from Docker Hub right now.
I want to tag Postgres and I'm going to tag it
with my prefix of my registry.
At that point, if I do docker image ls,
I should see Postgres 12.7 alpine and Postgres quirk.
Now I should be able to do image push and push that up.
Sure enough, you can see bam,
I'm now pushing up my alpine image.
I should be able to go to my registry repositories,
then, sure enough, Postgres is right there.
Then now when I go back to my run command,
like this, go here, do that.
We're not going to use hadolint,
but we're going to use Postgres.
We should see it pulled down from
my registry in Azure and then run the container.
Sure enough, we do quirk azure Postgres 12.7 alpine.
That means going forward,
if I was to do anything,
I should realistically be pushing up my own containers,
authenticating against my own registry,
and then running those containers.
The service connection here is going
to be right here and then this will just be quirk.
The endpoint being our authenticated registry,
meaning that it should just be able to pull
Postgres 12.7 out of that.
Sure enough, again,
if I go here and I go to Repositories,
we should see Postgres 12.7.
Okay, cool. We should be good with that.
All I need to do is go here,
go to the job, and then paste it right here,
that should mean for this job,
it's going to start the Postgres service that I just made.
It'll take care of starting it,
it'll take care of stopping it.
I don't have to do any of that stuff.
Then I'm going to say feature, CI, add,
migrate stage with Postgres Service.
Once that goes up, we should immediately go to our pipeline
and see we had one run and it immediately failed,
that's probably my syntax.
I can just go here and I can hit "Edit."
It should automatically show me in
their linting where my error is maybe.
Now you can see here I'm getting this linting issue.
It's steps.
It's not tasks it's steps.
This wasn't linting because I didn't give this a name.
Let's go back to our pipelines and we should
immediately see this start running. Yeah, there it is.
Cool. It's going to go through my unit tests as it normally does.
Then it's going to publish my image and then it's going to
run migrations against it. Here we go.
PostgreSQL initialized containers, command line.
We started a Postgres Service.
Cool. Let's look at initialize containers.
Here's Postgres, here's the port forwarding.
Look at this port forwarding.
We are at the point now where we wanted to get
this container running in pipelines, PostgreSQL,
initialize containers, we need to actually figure out a way of
dynamically adding our ormconfig to the container.
The challenges though, is we still need to
render this object in this form,
we just need to inject these variables into it.
We're going to go back to our Docker file.
You see this here, this node service index.js.
This means that this can only be really ran one way.
What we're going to do is we're going to actually
change this from node
to the good old fashion Docker entrypoint.sh.
This is what allows me to have a little bit more
flexible of a Docker file where I can say, "Hey,
not only am I going to make
this just package one thing and push it off,
but I'm also going to make it so that I can use
different commands against this too because there are
other things inside of this Docker file as well."
What we're going to do is we're going to use templating.
This ormconfig.json now becomes ormconfig.json.template.
These are now going to become
inline template strings that when we run our migrations,
it'll first take the template file
and then render it into what it should be.
Now we need to figure out how we're going to
actually generate this template.
We're going to say generate graphql, generate ormconfig.
Now we have the migrate command and the clean command,
we need to do the generate ormconfig command next.
Yes, Azure has the secrets manager,
it's called Key Vault.
We've already got one created, actually.
You can see here that this is essentially the overview of it.
Then allows me to add secrets, certificate,
keys, all that stuff directly to it.
They do in fact have a Key Vault. It's pretty nice.
Ormconfig.json.template,
and then we want to generate ormconfig.json.
Now what I need to do is I need to figure out why or how I can
get default values in here because
it should support default values.
Out cat.
There it is. Now let's go ahead and get the rest of
the template stuff in here so we know that it is all good again.
Let's try this really quickly.
Docker image build permission denied.
Who am I? That doesn't make
sense for it to work on one and not on the other.
We want our entry point file here to be the thing that runs it.
We want this to be a part of our container.
I'm going to need to solve this problem.
Honestly, it would be completely
fine in work if I wasn't using Alpine.
At this point, I'm going to pause what we're doing.
>> [MUSIC]