Published: Jan 25, 2023 by Isaac Johnson
We last explored Dapr Secrets nearly two years ago. I thought it might be nice to circle back on them and see how we can use Dapr.io with Azure KeyVault (AKV) and GCP Secrets Manager. My hope was then to tie them into API Posts to Mastodon.
I ended up getting delayed a day because my whole system started to go whacky. I had 15m docker pulls, networking was dropping out. Additionally, at home, my Wi-Fi was getting spottier by the day. The whole collective had not had a restart in 40days and it showed. When I restarted, key servers got new IPs; like the database for Harbor, which then blocked other pods, the Kubernetes Master so the Nodes couldn’t rejoin. It was a mess. But after lots of poking about, things are happy again - but delayed this post.
Dapr and Secrets
First, let’s look at how we check our Dapr version and upgrade, if needed.
First, we can check our Dapr version
$ dapr version
CLI version: 1.8.0
Runtime version: n/a
Then we can upgrade if needed (1.8.0 is pretty old at this point)
$ wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
Getting the latest Dapr CLI...
Your system is linux_amd64
Dapr CLI is detected:
CLI version: 1.8.0
Runtime version: n/a
Reinstalling Dapr CLI - /usr/local/bin/dapr...
Installing v1.9.1 Dapr CLI...
Downloading https://github.com/dapr/cli/releases/download/v1.9.1/dapr_linux_amd64.tar.gz ...
[sudo] password for builder:
Sorry, try again.
[sudo] password for builder:
dapr installed into /usr/local/bin successfully.
CLI version: 1.9.1
Runtime version: n/a
To get started with Dapr, please visit https://docs.dapr.io/getting-started/
We can see the CLI is running locally
$ dapr version
CLI version: 1.9.1
Runtime version: n/a
Next, we bring our Kubernetes cluster up to the same version
I can try and install
$ dapr init -k
⌛ Making the jump to hyperspace...
ℹ️ Note: To install Dapr using Helm, see here: https://docs.dapr.io/getting-started/install-dapr-kubernetes/#install-with-helm-advanced
ℹ️ Container images will be pulled from Docker Hub
❌ Deploying the Dapr control plane to your cluster...
❌ cannot re-use a name that is still in use
Verify it’s running in k8s
$ kubectl get pods --namespace dapr-system
NAME READY STATUS RESTARTS AGE
dapr-sentry-7f8db4ff47-h98l8 1/1 Running 0 11d
dapr-placement-server-0 1/1 Running 0 11d
dapr-dashboard-6b868c46fb-gxzrq 1/1 Running 0 11d
dapr-sidecar-injector-75948876c-g5hrz 1/1 Running 0 11d
dapr-operator-6bd7f7bfdd-66f6w 1/1 Running 336 (2d14h ago) 11d
You can check which version is running by port-forwarding to the dashboard
builder@DESKTOP-72D2D9T:~/Workspaces/jekyll-blog$ kubectl port-forward dapr-dashboard-6b868c46fb-gxzrq -n dapr-system 80
80:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
Handling connection for 8080
Azure Key Vault
Before we go farther, let’s go into Azure and create a key vault.
We could use the UI
But I’ll just quickly use the command line:
$ az group create --name daprkvrg --location centralus
{
"id": "/subscriptions/d955c0ba-eeee-eeee-eeee-eeeeeeff/resourceGroups/daprkvrg",
"location": "centralus",
"managedBy": null,
"name": "daprkvrg",
"properties": {
"provisioningState": "Succeeded"
},
"tags": null,
"type": "Microsoft.Resources/resourceGroups"
}
$ az keyvault create --location centralus --name daprakv -g daprkvrg
{
"id": "/subscriptions/d955c0ba-eeee-eeee-eeee-eeeeeeff/resourceGroups/daprkvrg/providers/Microsoft.KeyVault/vaults/daprakv",
"location": "centralus",
"name": "daprakv",
"properties": {
"accessPolicies": [
{
"applicationId": null,
"objectId": "1f5d835c-b129-41e6-b2fe-5858a5f4e41a",
"permissions": {
"certificates": [
"all"
],
"keys": [
"all"
],
"secrets": [
"all"
],
"storage": [
"all"
]
},
"tenantId": "28c575f6-ade1-4838-8e7c-7e6d1ba0eb4a"
}
],
"createMode": null,
"enablePurgeProtection": null,
"enableRbacAuthorization": null,
"enableSoftDelete": true,
"enabledForDeployment": false,
"enabledForDiskEncryption": null,
"enabledForTemplateDeployment": null,
"hsmPoolResourceId": null,
"networkAcls": null,
"privateEndpointConnections": null,
"provisioningState": "Succeeded",
"publicNetworkAccess": "Enabled",
"sku": {
"family": "A",
"name": "standard"
},
"softDeleteRetentionInDays": 90,
"tenantId": "28c575f6-ade1-4838-8e7c-7e6d1ba0eb4a",
"vaultUri": "https://daprakv.vault.azure.net/"
},
"resourceGroup": "daprkvrg",
"systemData": {
"createdAt": "2023-01-23T12:47:59.504000+00:00",
"createdBy": "isaac.johnson@gmail.com",
"createdByType": "User",
"lastModifiedAt": "2023-01-23T12:47:59.504000+00:00",
"lastModifiedBy": "isaac.johnson@gmail.com",
"lastModifiedByType": "User"
},
"tags": {},
"type": "Microsoft.KeyVault/vaults"
}
To add AKV to Dapr, we’ll need to create an SP we can tie to an Azure Key Vault:
$ az login
$ az ad sp create-for-rbac --name tpkdapr.isaacjohnsongmail.onmicrosoft.com
{
"appId": "61581835-asdf-asdf-asdf-asdfasdfadsf",
"displayName": "tpkdapr.isaacjohnsongmail.onmicrosoft.com",
"password": "1asdfasdfasdfasdfasfasdasdfasdfah",
"tenant": "28c575f6-asdf-asdf-asdf-asdfasdfasfasdf"
}
Next, I’ll need to grant this SP access to the AKV.. This should work
$ az role assignment create --role "Key Vault Secrets Officer" --assignee 61581835-asdf-asdf-asdf-asdfasdfadsf --scope /subscriptions/d955c0ba-eeee-eeee-eeee-eeeeeeff/resourceGroups/daprkvrg/providers/Microsoft.KeyVault/vaults/daprakv
{
"canDelegate": null,
"condition": null,
"conditionVersion": null,
"description": null,
"id": "/subscriptions/d955c0ba-eeee-eeee-eeee-eeeeeeff/resourceGroups/daprkvrg/providers/Microsoft.KeyVault/vaults/daprakv/providers/Microsoft.Authorization/roleAssignments/46d59609-3a9d-48ff-88bc-25d4c57f3b17",
"name": "46d59609-3a9d-48ff-88bc-25d4c57f3b17",
"principalId": "fa102a38-aaaa-aaaa-aaaa-aaaaa01157b5",
"principalType": "ServicePrincipal",
"resourceGroup": "daprkvrg",
"roleDefinitionId": "/subscriptions/d955c0ba-eeee-eeee-eeee-eeeeeeff/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7",
"scope": "/subscriptions/d955c0ba-eeee-eeee-eeee-eeeeeeff/resourceGroups/daprkvrg/providers/Microsoft.KeyVault/vaults/daprakv",
"type": "Microsoft.Authorization/roleAssignments"
}
But I noticed the “principalID” wasn’t the AppID I expected. I went back into the Access Policies and did it again manually and then saw my application list properly
Setting up Dapr
In Kubernetes, I’ll want to set the clientsecret (password) to a Kubernetes secret
$ kubectl create secret generic daprakvkey --from-literal=password=1asdfasdfasdfasdfasfasdasdfasdfah
secret/daprakvkey created
You can do a sanity check that there were no funny characters and that it exists by pulling the secret back and decoding:
$ kubectl get secret daprakvkey -o json | jq -r .data.password | base64
--decode
1asdfasdfasdfasdfasfasdasdfasdfah
We can now use it in Dapr
$ cat azurekeyvault.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: azurekeyvault
spec:
type: secretstores.azure.keyvault
version: v1
metadata:
- name: vaultName
value: "daprakv"
- name: azureTenantId
value: "28c575f6-aaaa-aaaa-aaaa-aaaaaa0eb4a"
- name: azureClientId
value: "61581835-eeee-eeee-eeee-eeeeeb1d583"
- name: azureClientSecret
secretKeyRef:
name: "daprakvkey"
key: "password"
auth:
secretStore: kubernetes
which we can then apply
$ kubectl apply -f azurekeyvault.yaml
component.dapr.io/azurekeyvault created
To do a quick test, I’ll create a secret in AKV
$ az keyvault secret set --vault-name daprakv --name mytestsecret --value hellofromakv
{
"attributes": {
"created": "2023-01-23T13:05:41+00:00",
"enabled": true,
"expires": null,
"notBefore": null,
"recoveryLevel": "Recoverable+Purgeable",
"updated": "2023-01-23T13:05:41+00:00"
},
"contentType": null,
"id": "https://daprakv.vault.azure.net/secrets/mytestsecret/a240d11b435f4b74a667dd666329cf1f",
"kid": null,
"managed": null,
"name": "mytestsecret",
"tags": {
"file-encoding": "utf-8"
},
"value": "hellofromakv"
}
Then I’ll rotate a Dapr instrumented pod in my cluster
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl delete pod addapp-677b754f58-scw6k
pod "addapp-677b754f58-scw6k" deleted
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 42s
My pulls are taking upwards of 10m. This, i discovered, was due to Docker rate limiting (recall in my last post why I moved to HarborCR)
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl delete pod addapp-677b754f58-scw6k
pod "addapp-677b754f58-scw6k" deleted
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 42s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 54s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 57s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 66s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 2m7s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 4m20s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep add
addapp-677b754f58-59m8m 0/2 ContainerCreating 0 4m42s
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl describe pod addapp-677b754f58-59m8m | tail -n5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m56s default-scheduler Successfully assigned default/addapp-677b754f58-59m8m to hp-hp-elitebook-850-g2
Normal Pulling 4m54s kubelet Pulling image "idjohnson/coboladder:0.0.3"
# Checking if we have a rate limit
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4414 0 4414 0 0 22070 0 --:--:-- --:--:-- --:--:-- 22070
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ curl --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
HTTP/1.1 200 OK
content-length: 2782
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020
docker-distribution-api-version: registry/2.0
etag: "sha256:767a3815c34823b355bed31760d5fa3daca0aec2ce15b217c9cd83229e0e2020"
date: Mon, 23 Jan 2023 13:24:52 GMT
strict-transport-security: max-age=31536000
ratelimit-limit: 100;w=21600
ratelimit-remaining: 97;w=21600
docker-ratelimit-source: 73.242.50.46
It took a fair amount of time, but eventually my container came up.
One gotcha I had was the dogs all showed commands using localhost:3601 (e.g. Secrets How-To)
[root@addapp-677b754f58-4gppg cobol]# curl http://127.0.0.1:3601/v1.0/secrets/azurekeyvault/mytestsecret
curl: (7) Failed to connect to 127.0.0.1 port 3601: Connection refused
However, it really should be localhost:$DAPR_HTTP_PORT. In our setup, we can see DAPR is serving up on port 3500:
[root@addapp-677b754f58-4gppg cobol]# export | grep DAPR
declare -x DAPR_GRPC_PORT="50001"
declare -x DAPR_HTTP_PORT="3500"
Once I realized that, it was easy to fetch a Dapr secret
[root@addapp-677b754f58-4gppg cobol]# curl http://localhost:$DAPR_HTTP_PORT/v1.0/secrets/azurekeyvault/mytestsecret && echo
{"mytestsecret":"hellofromakv"}
[root@addapp-677b754f58-4gppg cobol]# curl http://127.0.0.1:3500/v1.0/secrets/azurekeyvault/mytestsecret
{"mytestsecret":"hellofromakv"}
There is nothing that suggests Dapr can update secrets, but if it could, I would suspect it would be wrapped in a POST.
Just to experiment, I tried a few possibilities (and as expected, they did not work)
[root@addapp-677b754f58-4gppg cobol]# curl -X POST http://localhost:3500/v1.0/secrets/azurekeyvault/mytestsecret -H "Content-Type: application/json" -d '{"mytestsecret":"anewvalue"}'
{"errorCode":"ERR_DIRECT_INVOKE","message":"failed getting app id either from the URL path or the header dapr-app-id"}
[root@addapp-677b754f58-4gppg cobol]# curl -X POST http://localhost:3500/v1.0/secrets/azurekeyvault -H "Content-Type: application/json" -d '{"mytestsecret":"anewvalue"}'
{"errorCode":"ERR_DIRECT_INVOKE","message":"failed getting app id either from the URL path or the header dapr-app-id"}
[root@addapp-677b754f58-4gppg cobol]# curl -X POST http://localhost:3500/v1.0/secrets/azurekeyvault/mytestsecret -H "Content-Type: application/json" -d '{"value":"anewvalue"}'
{"errorCode":"ERR_DIRECT_INVOKE","message":"failed getting app id either from the URL path or the header dapr-app-id"}
[root@addapp-677b754f58-4gppg cobol]# curl -X POST http://localhost:3500/v1.0/secrets/azurekeyvault/mytestsecret/anewvalue -H "Content-Type: application/json"
{"errorCode":"ERR_DIRECT_INVOKE","message":"failed getting app id either from the URL path or the header dapr-app-id"}
GCP Secret Store
Nowadays I do as much with GCP as I do Azure.
We should give the GCP Secret Manager a look as well.
I’ll go to a GCP Project and create a new Service Account for this work:
I’ll set some details (I’ll likely use this same credential later for Pub/Sub and more)
Since I plan to use this for a few services, I’ll add Storage Admin and Pub/Sub Editor roles to Secret Manager Secret Accessor.
You can leave the last section blank and click “Done” to create
Next, use the 3-dot menu to “Manage Keys”
Click “Add Key” and select “Create new key”
Choose JSON and click “Create”
That should download locally and we’ll use to fill in the blanks
$ cat /mnt/c/Users/isaac/Downloads/myanthosproject2-b8b0858893d5.json
{
"type": "service_account",
"project_id": "myanthosproject2",
"private_key_id": "b8b0858893d512341234123412341234f3e4",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgasasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfas8g==\n-----END PRIVATE KEY-----\n",
"client_email": "daprservice2@myanthosproject2.iam.gserviceaccount.com",
"client_id": "103334445556697",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/daprservice2%40myanthosproject2.iam.gserviceaccount.com"
}
That will then translate to the below:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: gcpsecretmanager
spec:
type: secretstores.gcp.secretmanager
version: v1
metadata:
- name: type
value: "service_account"
- name: project_id
value: "myanthosproject2"
- name: private_key_id
value: "b8b0858893d512341234123412341234f3e4"
- name: client_email
value: "daprservice2@myanthosproject2.iam.gserviceaccount.com"
- name: client_id
value: "103334445556697"
- name: auth_uri
value: "https://accounts.google.com/o/oauth2/auth"
- name: token_uri
value: "https://oauth2.googleapis.com/token"
- name: auth_provider_x509_cert_url
value: "https://www.googleapis.com/oauth2/v1/certs"
- name: client_x509_cert_url
value: "https://www.googleapis.com/robot/v1/metadata/x509/daprservice2%40myanthosproject2.iam.gserviceaccount.com"
- name: private_key
value: "-----BEGIN PRIVATE KEY-----\nnMIIEvAIBADANBgasasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfaasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfadfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfas8g\n-----END PRIVATE KEY-----\n"
I’ll then apply it
$ kubectl apply -f gcpsecretmanager.yaml
component.dapr.io/gcpsecretmanager created
And rotate a pod
$ kubectl delete pod addapp-677b754f58-4gppg
pod "addapp-677b754f58-4gppg" deleted
While that rotates, I’ll circle back to GCP and make a secret
First, I’ll need to enable the Secret Manager API as I haven’t in the past
We can see the price is free up to 10k uses a month
Next, I’ll create a secret
I’ll give it a name and a value, then click “Create Secret”
We can now see it in Secret Manager. If we click the dots menu, we can “View secret value”
and confirm it is indeed enabled and matches what we typed earlier
I have to admit, these long container pulls are driving me batty.
$ kubectl get pods | grep add
addapp-677b754f58-4sq2v 0/2 ContainerCreating 0 6m33s
I’m going to update the Deployment to pull the image only when needed
$ kubectl get deployments addapp -o yaml > addapp.yaml
$ kubectl get deployments addapp -o yaml > addapp.yaml.bak
$ vi addapp.yaml
$ diff addapp.yaml addapp.yaml.bak
44c44
< imagePullPolicy: IfNotPresent
---
> imagePullPolicy: Always
$ kubectl apply -f ./addapp.yaml
deployment.apps/addapp configured
I did it to myself, so I’ll have to wait this first time, but it should go better the next time
$ kubectl describe pod addapp-549c9cbfdd-h9fs4 | tail -n4
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 47s default-scheduler Successfully assigned default/addapp-549c9cbfdd-h9fs4 to hp-hp-elitebook-850-g2
Normal Pulling 45s kubelet Pulling image "idjohnson/coboladder:0.0.3"
$ kubectl describe pod addapp-549c9cbfdd-h9fs4 | tail -n4
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 15m default-scheduler Successfully assigned default/addapp-549c9cbfdd-h9fs4 to hp-hp-elitebook-850-g2
Normal Pulling 15m kubelet Pulling image "idjohnson/coboladder:0.0.3"
$ kubectl describe pod addapp-549c9cbfdd-h9fs4 | tail -n4
Normal Started 20m kubelet Started container add
Normal Pulled 20m kubelet Container image "docker.io/daprio/daprd:1.9.5" already present on machine
Normal Created 20m kubelet Created container daprd
Normal Started 20m kubelet Started container daprd
We can now exec into the pod
$ kubectl exec -it addapp-549c9cbfdd-h9fs4 -- /bin/bash
Defaulted container "add" out of: add, daprd
[root@addapp-549c9cbfdd-h9fs4 cobol]#
And verify we can pull the secret
[root@addapp-549c9cbfdd-h9fs4 cobol]# curl http://localhost:$DAPR_HTTP_PORT/v1.0/secrets/gcpsecretmanager/mytestsecret && echo
{"mytestsecret":"Some Value For Dapr to See"}
Additionally, we haven’t switched secrets providers, we merely added one. So I can still fetch secrets from AKV on the same pod
[root@addapp-549c9cbfdd-h9fs4 cobol]# curl http://localhost:$DAPR_HTTP_PORT/v1.0/secrets/azurekeyvault/mytestsecret && echo
{"mytestsecret":"hellofromakv"}
Mastodon
The goal of all this was to post to Mastodon. There are a lot of good Mastodon servers out there. I use Noc.social myself, but there are many good options.
Be aware, Mastodon is pretty hot right now, so the UI may change. Today, we go to Preferences on the right-hand side
Then go to Development
Here, we’ll make a “New application”
I’ll give it a name
I left the default scopes (read, write, follow) but also added “crypto”. It might not be neccessary to add crypto, but i like the idea i could use E2E encryption.
This should create an Application
And from there, I can see my Client ID, Secret and Token
I should note, you can follow the more standard OAuth2 flow and use the Client ID and Secret paired with your Username and password to get the access token dynamically: https://github.com/felx/mastodon-documentation/blob/master/Using-the-API/Testing-with-cURL.md
But for our purposes, I’ll satisfied with just using the access token provided (and rotatable) through the Developer UI.
We can now do a test post
$ curl -X POST -H 'Authorization: Bearer cl5EnOtaRealTokenButSimilarToThisQdGo' -H 'Content-Type: application/json' -d '{"status":"Te
st Post, please ignore"}' https://noc.social/api/v1/statuses
{"id":"109744259200323382","created_at":"2023-01-24T12:48:21.963Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https://noc.social/users/Ijohnson/statuses/109744259200323382","url":"https://noc.social/@Ijohnson/109744259200323382","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003eTest Post, please ignore\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"TPKGithubPosting","website":"https://github.com/idjohnson"},"account":{"id":"109299867199050307","username":"Ijohnson","acct":"Ijohnson","display_name":"Ijohnson","locked":false,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-07T00:00:00.000Z","note":"","url":"https://noc.social/@Ijohnson","avatar":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","avatar_static":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","header":"https://noc.social/headers/original/missing.png","header_static":"https://noc.social/headers/original/missing.png","followers_count":4,"following_count":6,"statuses_count":31,"last_status_at":"2023-01-24","noindex":false,"emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[],"emojis":[],"card":null,"poll":null}
And I can see it posted
I can then delete it manually
We can fetch an old status
$ curl -X GET -H 'Authorization: Bearer cl5EnOtaRealTokenButSimilarToThisQdGo' -H 'Content-Type: application/json' https://noc.social/api/v1/statuses/109706528335457979
{"id":"109706528335457979","created_at":"2023-01-17T20:52:54.652Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https://noc.social/users/Ijohnson/statuses/109706528335457979","url":"https://noc.social/@Ijohnson/109706528335457979","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003eToday I wrapped a 2 part series on \u003ca href=\"https://noc.social/tags/IBM\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eIBM\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://noc.social/tags/Instana\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eInstana\u003c/span\u003e\u003c/a\u003e. Part 1 covers Setup, \u003ca href=\"https://noc.social/tags/kubernetes\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ekubernetes\u003c/span\u003e\u003c/a\u003e Website monitoring and \u003ca href=\"https://noc.social/tags/OpenTelemetry\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eOpenTelemetry\u003c/span\u003e\u003c/a\u003e tracing. Today I posted part 2 which covers \u003ca href=\"https://noc.social/tags/gcp\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003egcp\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://noc.social/tags/databases\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003edatabases\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://noc.social/tags/alerting\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ealerting\u003c/span\u003e\u003c/a\u003e via \u003ca href=\"https://noc.social/tags/pagerduty\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003epagerduty\u003c/span\u003e\u003c/a\u003e and log integration with \u003ca href=\"https://noc.social/tags/mezmo\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003emezmo\u003c/span\u003e\u003c/a\u003e / \u003ca href=\"https://noc.social/tags/logdna\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003elogdna\u003c/span\u003e\u003c/a\u003e and \u003ca href=\"https://noc.social/tags/coralogix\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ecoralogix\u003c/span\u003e\u003c/a\u003e. \u003c/p\u003e\u003cp\u003ePart 1: \u003ca href=\"https://freshbrewed.science/2023/01/10/Instana-Part1.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"ellipsis\"\u003efreshbrewed.science/2023/01/10\u003c/span\u003e\u003cspan class=\"invisible\"\u003e/Instana-Part1.html\u003c/span\u003e\u003c/a\u003e\u003c/p\u003e\u003cp\u003ePart 2: \u003ca href=\"https://freshbrewed.science/2023/01/17/Instana-Part2.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"ellipsis\"\u003efreshbrewed.science/2023/01/17\u003c/span\u003e\u003cspan class=\"invisible\"\u003e/Instana-Part2.html\u003c/span\u003e\u003c/a\u003e\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"Web","website":null},"account":{"id":"109299867199050307","username":"Ijohnson","acct":"Ijohnson","display_name":"Ijohnson","locked":false,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-07T00:00:00.000Z","note":"","url":"https://noc.social/@Ijohnson","avatar":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","avatar_static":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","header":"https://noc.social/headers/original/missing.png","header_static":"https://noc.social/headers/original/missing.png","followers_count":4,"following_count":6,"statuses_count":30,"last_status_at":"2023-01-24","noindex":false,"emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[{"name":"Coralogix","url":"https://noc.social/tags/Coralogix"},{"name":"logdna","url":"https://noc.social/tags/logdna"},{"name":"mezmo","url":"https://noc.social/tags/mezmo"},{"name":"pagerduty","url":"https://noc.social/tags/pagerduty"},{"name":"alerting","url":"https://noc.social/tags/alerting"},{"name":"databases","url":"https://noc.social/tags/databases"},{"name":"gcp","url":"https://noc.social/tags/gcp"},{"name":"opentelemetry","url":"https://noc.social/tags/opentelemetry"},{"name":"kubernetes","url":"https://noc.social/tags/kubernetes"},{"name":"instana","url":"https://noc.social/tags/instana"},{"name":"ibm","url":"https://noc.social/tags/ibm"}],"emojis":[],"card":{"url":"https://freshbrewed.science/2023/01/10/Instana-Part1.html","title":"Instana: Part 1: Setup, K8s, Website and Open Telemetry","description":"Today we will dig into creating an Instana account and setting up Host and Kubernetes monitors. We’ll look into serverless (AWS Lambdas) and tracing through Open Telemetry.","type":"link","author_name":"","author_url":"","provider_name":"Fresh/Brewed","provider_url":"","html":"","width":0,"height":0,"image":null,"embed_url":"","blurhash":null},"poll":null}b
Which shows the guts of the status (content) to be Unicode HTML
\u003cp\u003eToday I wrapped a 2 part series on \u003ca href=\"https://noc.social/tags/IBM\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eIBM\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://noc.social/tags/Instana\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eInstana\u003c/span\u003e\u003c/a\u003e. Part 1 covers Setup, \u003ca href=\"https://noc.social/tags/kubernetes\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ekubernetes\u003c/span\u003e\u003c/a\u003e Website monitoring and \u003ca href=\"https://noc.social/tags/OpenTelemetry\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003eOpenTelemetry\u003c/span\u003e\u003c/a\u003e tracing. Today I posted part 2 which covers \u003ca href=\"https://noc.social/tags/gcp\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003egcp\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://noc.social/tags/databases\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003edatabases\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://noc.social/tags/alerting\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ealerting\u003c/span\u003e\u003c/a\u003e via \u003ca href=\"https://noc.social/tags/pagerduty\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003epagerduty\u003c/span\u003e\u003c/a\u003e and log integration with \u003ca href=\"https://noc.social/tags/mezmo\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003emezmo\u003c/span\u003e\u003c/a\u003e / \u003ca href=\"https://noc.social/tags/logdna\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003elogdna\u003c/span\u003e\u003c/a\u003e and \u003ca href=\"https://noc.social/tags/coralogix\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003ecoralogix\u003c/span\u003e\u003c/a\u003e. \u003c/p\u003e\u003cp\u003ePart 1: \u003ca href=\"https://freshbrewed.science/2023/01/10/Instana-Part1.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"ellipsis\"\u003efreshbrewed.science/2023/01/10\u003c/span\u003e\u003cspan class=\"invisible\"\u003e/Instana-Part1.html\u003c/span\u003e\u003c/a\u003e\u003c/p\u003e\u003cp\u003ePart 2: \u003ca href=\"https://freshbrewed.science/2023/01/17/Instana-Part2.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"ellipsis\"\u003efreshbrewed.science/2023/01/17\u003c/span\u003e\u003cspan class=\"invisible\"\u003e/Instana-Part2.html\u003c/span\u003e\u003c/a\u003e\u003c/p\u003e
I can use a quick online converter to see the raw HTML
I’m curious if my “#” entries will be automatically turned into internal labels or i have to do it manaully. Let’s test that.
$ curl -X POST -H 'Authorization: Bearer cl5EnOtaRealTokenButSimilarToThisQdGo' -H 'Content-Type: application/json' -d '{"status":"Test 2, #testing <a href=\"http://freshbrewed.science\">blog</a>"}' https://noc.social/api/v1/statuses
{"id":"109744295782598916","created_at":"2023-01-24T12:57:40.161Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https://noc.social/users/Ijohnson/statuses/109744295782598916","url":"https://noc.social/@Ijohnson/109744295782598916","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003eTest 2, \u003ca href=\"https://noc.social/tags/testing\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003etesting\u003c/span\u003e\u003c/a\u003e \u0026lt;a href=\u0026quot;\u003ca href=\"http://freshbrewed.science\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttp://\u003c/span\u003e\u003cspan class=\"\"\u003efreshbrewed.science\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e\u0026quot;\u0026gt;blog\u0026lt;/a\u0026gt;\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"TPKGithubPosting","website":"https://github.com/idjohnson"},"account":{"id":"109299867199050307","username":"Ijohnson","acct":"Ijohnson","display_name":"Ijohnson","locked":false,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-07T00:00:00.000Z","note":"","url":"https://noc.social/@Ijohnson","avatar":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","avatar_static":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","header":"https://noc.social/headers/original/missing.png","header_static":"https://noc.social/headers/original/missing.png","followers_count":4,"following_count":6,"statuses_count":31,"last_status_at":"2023-01-24","noindex":false,"emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[{"name":"testing","url":"https://noc.social/tags/testing"}],"emojis":[],"card":null,"poll":null}
Interestingly the # tag works just fine, but the URL was a fail
My final test is to pack in Unicode with a URL
$ curl -X POST -H 'Authorization: Bearer cl5EnOtaRealTokenButSimilarToThisQdGo' -H 'Content-Type: application/json' -d '{"status":"Test 2, #testing https://freshbrewed.science \u003ca href=\"https://freshbrewed.science/2023/01/10/Instana-Part1.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003eSome Post\u003c/a\u003e"}' https://noc.social/api/v1/statuses
{"id":"109744325594361896","created_at":"2023-01-24T13:05:15.058Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":"en","uri":"https://noc.social/users/Ijohnson/statuses/109744325594361896","url":"https://noc.social/@Ijohnson/109744325594361896","replies_count":0,"reblogs_count":0,"favourites_count":0,"edited_at":null,"favourited":false,"reblogged":false,"muted":false,"bookmarked":false,"pinned":false,"content":"\u003cp\u003eTest 2, \u003ca href=\"https://noc.social/tags/testing\" class=\"mention hashtag\" rel=\"tag\"\u003e#\u003cspan\u003etesting\u003c/span\u003e\u003c/a\u003e \u003ca href=\"https://freshbrewed.science\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003efreshbrewed.science\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e \u0026lt;a href=\u0026quot;\u003ca href=\"https://freshbrewed.science/2023/01/10/Instana-Part1.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"ellipsis\"\u003efreshbrewed.science/2023/01/10\u003c/span\u003e\u003cspan class=\"invisible\"\u003e/Instana-Part1.html\u003c/span\u003e\u003c/a\u003e\u0026quot; target=\u0026quot;_blank\u0026quot; rel=\u0026quot;nofollow noopener noreferrer\u0026quot;\u0026gt;Some Post\u0026lt;/a\u0026gt;\u003c/p\u003e","filtered":[],"reblog":null,"application":{"name":"TPKGithubPosting","website":"https://github.com/idjohnson"},"account":{"id":"109299867199050307","username":"Ijohnson","acct":"Ijohnson","display_name":"Ijohnson","locked":false,"bot":false,"discoverable":null,"group":false,"created_at":"2022-11-07T00:00:00.000Z","note":"","url":"https://noc.social/@Ijohnson","avatar":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","avatar_static":"https://noc.social/system/accounts/avatars/109/299/867/199/050/307/original/cc07f8a94f1ffadf.jpeg","header":"https://noc.social/headers/original/missing.png","header_static":"https://noc.social/headers/original/missing.png","followers_count":4,"following_count":6,"statuses_count":31,"last_status_at":"2023-01-24","noindex":false,"emojis":[],"fields":[]},"media_attachments":[],"mentions":[],"tags":[{"name":"testing","url":"https://noc.social/tags/testing"}],"emojis":[],"card":null,"poll":null}
While the URL worked, the packed Unicode did not
Mastodon and Dapr Secrets
Now that we have a nice secret store as provided via Dapr, could I use that in my Summerwind Github Action Runners?
Note: This ultimately did not work. I think it has to do with how the Summerwind injects DnD containers. It was in the middle of testing this my whole system pooped out and delayed me. But I’ll show you my work regardless. I ended up just using a GH Actions Secret instead
First, we need to realize that the Github Action Runner isn’t a standard deployment, rather a “RunnerDeployment” as setup via actions.summerwind.dev
$ kubectl get RunnerDeployment new-jekyllrunner-deployment -o yaml
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"actions.summerwind.dev/v1alpha1","kind":"RunnerDeployment","metadata":{"annotations":{},"name":"new-jekyllrunner-deployment","namespace":"default"},"spec":{"replicas":2,"selector":null,"template":{"metadata":{},"spec":{"dockerEnabled":true,"dockerdContainerResources":{},"env":[{"name":"AWS_DEFAULT_REGION","value":"us-east-1"},{"name":"AWS_ACCESS_KEY_ID","valueFrom":{"secretKeyRef":{"key":"USER_NAME","name":"awsjekyll"}}},{"name":"AWS_SECRET_ACCESS_KEY","valueFrom":{"secretKeyRef":{"key":"PASSWORD","name":"awsjekyll"}}},{"name":"DATADOG_API_KEY","valueFrom":{"secretKeyRef":{"key":"DDAPIKEY","name":"ddjekyll"}}}],"image":"harbor.freshbrewed.science/freshbrewedprivate/myghrunner:1.1.13","imagePullPolicy":"IfNotPresent","imagePullSecrets":[{"name":"myharborreg"}],"labels":["new-jekyllrunner-deployment"],"repository":"idjohnson/jekyll-blog","resources":{}}}}}
creationTimestamp: "2022-08-31T12:21:29Z"
generation: 1
name: new-jekyllrunner-deployment
namespace: default
resourceVersion: "72454949"
uid: 05168427-0f0b-4cee-a692-904ae409ed1b
spec:
effectiveTime: null
replicas: 2
selector: null
template:
metadata: {}
spec:
dockerEnabled: true
dockerdContainerResources: {}
env:
- name: AWS_DEFAULT_REGION
value: us-east-1
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
key: USER_NAME
name: awsjekyll
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
key: PASSWORD
name: awsjekyll
- name: DATADOG_API_KEY
valueFrom:
secretKeyRef:
key: DDAPIKEY
name: ddjekyll
image: harbor.freshbrewed.science/freshbrewedprivate/myghrunner:1.1.13
imagePullPolicy: IfNotPresent
imagePullSecrets:
- name: myharborreg
labels:
- new-jekyllrunner-deployment
repository: idjohnson/jekyll-blog
resources: {}
status:
availableReplicas: 2
desiredReplicas: 2
readyReplicas: 2
replicas: 2
updatedReplicas: 2
But I should be able to just add in the Dapr annotations to the spec.metadata to instrument it with Dapr.
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get RunnerDeployment new-jekyllrunner-deployment -o yaml > myjekyllrunner.yaml
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get RunnerDeployment new-jekyllrunner-deployment -o yaml > myjekyllrunner.yaml.bak
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ vi myjekyllrunner.yaml
Then showing the changes:
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ diff -C 10 myjekyllrunner.yaml myjekyllrunner.yaml.bak
*** myjekyllrunner.yaml 2023-01-24 07:17:05.246476950 -0600
--- myjekyllrunner.yaml.bak 2023-01-24 07:16:25.623693832 -0600
***************
*** 8,33 ****
generation: 1
name: new-jekyllrunner-deployment
namespace: default
resourceVersion: "72454949"
uid: 05168427-0f0b-4cee-a692-904ae409ed1b
spec:
effectiveTime: null
replicas: 2
selector: null
template:
! metadata:
! annotations:
! dapr.io/app-id: jekyllrunner
! dapr.io/app-port: "4000"
! dapr.io/config: appconfig
! dapr.io/enabled: "true"
spec:
dockerEnabled: true
dockerdContainerResources: {}
env:
- name: AWS_DEFAULT_REGION
value: us-east-1
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
key: USER_NAME
--- 8,28 ----
generation: 1
name: new-jekyllrunner-deployment
namespace: default
resourceVersion: "72454949"
uid: 05168427-0f0b-4cee-a692-904ae409ed1b
spec:
effectiveTime: null
replicas: 2
selector: null
template:
! metadata: {}
spec:
dockerEnabled: true
dockerdContainerResources: {}
env:
- name: AWS_DEFAULT_REGION
value: us-east-1
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
key: USER_NAME
Applying
$ kubectl apply -f myjekyllrunner.yaml
runnerdeployment.actions.summerwind.dev/new-jekyllrunner-deployment configured
Though, this doesn’t look like it applied it at the pod level
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep runner
new-jekyllrunner-deployment-4zzwr-5qx5w 2/2 Running 0 23s
new-jekyllrunner-deployment-4zzwr-jkjc6 2/2 Running 0 23s
new-jekyllrunner-deployment-tbq6z-k6xfb 2/2 Terminating 0 22h
new-jekyllrunner-deployment-tbq6z-sxjj5 1/2 Terminating 0 22h
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl get pods | grep runner
new-jekyllrunner-deployment-4zzwr-5qx5w 2/2 Running 0 29s
new-jekyllrunner-deployment-4zzwr-jkjc6 2/2 Running 0 29s
Though I see the annotations
$ kubectl describe pod new-jekyllrunner-deployment-4zzwr-5qx5w
Name: new-jekyllrunner-deployment-4zzwr-5qx5w
Namespace: default
Priority: 0
Node: hp-hp-elitebook-850-g2/192.168.1.57
Start Time: Tue, 24 Jan 2023 07:18:02 -0600
Labels: actions-runner=
actions-runner-controller/inject-registration-token=true
pod-template-hash=59fc694745
runner-deployment-name=new-jekyllrunner-deployment
runner-template-hash=5df47b8ccc
Annotations: actions-runner-controller/token-expires-at: 2023-01-24T08:18:02-06:00
dapr.io/app-id: jekyllrunner
dapr.io/app-port: 4000
dapr.io/config: appconfig
dapr.io/enabled: true
sync-time: 2023-01-24T13:18:01Z
However, clearly we can see it only has the ‘runner’ and ‘docker’ containers in the pod
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ kubectl logs new-jekyllrunner-deployment-4zzwr-5qx5w
Defaulted container "runner" out of: runner, docker
2023-01-24 13:18:04.154 DEBUG --- Github endpoint URL https://github.com/
2023-01-24 13:18:07.913 DEBUG --- Passing --ephemeral to config.sh to enable the ephemeral runner.
2023-01-24 13:18:07.923 DEBUG --- Configuring the runner.
--------------------------------------------------------------------------------
| ____ _ _ _ _ _ _ _ _ |
| / ___(_) |_| | | |_ _| |__ / \ ___| |_(_) ___ _ __ ___ |
| | | _| | __| |_| | | | | '_ \ / _ \ / __| __| |/ _ \| '_ \/ __| |
| | |_| | | |_| _ | |_| | |_) | / ___ \ (__| |_| | (_) | | | \__ \ |
| \____|_|\__|_| |_|\__,_|_.__/ /_/ \_\___|\__|_|\___/|_| |_|___/ |
| |
| Self-hosted runner registration |
| |
--------------------------------------------------------------------------------
# Authentication
√ Connected to GitHub
# Runner Registration
I can see the annotations are there, but something about the Runner injection is likely blocking us.
We can sort this out with a quick pivot to using Github Actions secrets.
I’ll add my token as a secret for the Runner to consume
I’ve been going back and forth between the right way to fetch the content for a post.
I could pull from a PR description using a Github Token
- name: Test GH first
run: |
curl -s -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GHTOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/idjohnson/jekyll-blog/pulls/126 | jq -r .body
env:
GHTOKEN: $
Or using a new section in the post header
layout: post
title: "Dapr and Mostodon"
image: "/content/images/2023/01/mastodon-01.png"
date: '2023-01-19 01:01:31'
social: "In todays post we'll look at #Dapr secrets using #GCP Secret Manager and #Azure Keyvault. We'll then explore posting to #Mastodon using the REST API and #Github actions"
in an Action
- name: Test GH first
run: |
export LATESTFILE=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9].*markdown\)/\1/'`
cat _posts/$LATESTFILE | grep "^social: " | head -n 1 | sed 's/^social: "\(.*\)"/\1/'
I decided to do the second approach as it doesn’t require me to format Github PR messages for Social posting.
Here we can see it pulled the value and posted
which posted as such
I added the URL to the JSON version in fact the following block posted twice
- name: Test GH first
run: |
export LATESTFILE=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9].*markdown\)/\1/'`
export LATESTURL=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-\)\(.*\)\.markdown/\2.html/'`
echo '{"status":"' | tr -d '\n' > payload.json
cat _posts/$LATESTFILE | grep "^social: " | head -n 1 | sed 's/^social: "\(.*\)"/\1/' | tr -d '\n'>> payload.json
cat _posts/$LATESTFILE | grep "^date: " | head -n 1 | sed "s/^date: .\([0-9]*\)-\([0-9]*\)-\([0-9]*\) .*/ https:\/\/freshbrewed.science\/\1\/\2\/\3\/$LATESTURL/g" | sed 's/.markdown/.html/' | tr -d '\n' >> payload.json
echo '"}' >> payload.json
curl -X POST -H "Authorization: Bearer $MASTODONAPI" -H 'Content-Type: application/json' -d @payload.json https://noc.social/api/v1/statuses
export STATUSMSG=`cat _posts/$LATESTFILE | grep "^social: " | head -n 1 | sed 's/^social: "\(.*\)"/\1/'`
curl --header "Authorization: Bearer $MASTODONAPI" --form "status=$STATUSMSG" "https://noc.social/api/v1/statuses"
env:
MASTODONAPI: $
GHTOKEN: $
At this point, I’m rather satisfied with my flow. I’ll prep (this post) to use it in the action when merged to main.
- name: cloudfront invalidation
run: |
aws cloudfront create-invalidation --distribution-id E3U2HCN2ZRTBZN --paths "/index.html"
env: # Or as an environment variable
AWS_ACCESS_KEY_ID: $
AWS_SECRET_ACCESS_KEY: $
AWS_DEFAULT_REGION: $
- name: Post to Mastodon
run: |
export LATESTFILE=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9].*markdown\)/\1/'`
export LATESTURL=`ls -l _posts/ | grep ".* 20[0-9][0-9]-[0-9][0-9].*markdown" | tail -n1 | sed 's/.* \(20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-\)\(.*\)\.markdown/\2.html/'`
echo '{"status":"' | tr -d '\n' > payload.json
cat _posts/$LATESTFILE | grep "^social: " | head -n 1 | sed 's/^social: "\(.*\)"/\1/' | tr -d '\n'>> payload.json
cat _posts/$LATESTFILE | grep "^date: " | head -n 1 | sed "s/^date: .\([0-9]*\)-\([0-9]*\)-\([0-9]*\) .*/ https:\/\/freshbrewed.science\/\1\/\2\/\3\/$LATESTURL/g" | sed 's/.markdown/.html/' | tr -d '\n' >> payload.json
echo '"}' >> payload.json
cat payload.json | base64
curl -X POST -H "Authorization: Bearer $MASTODONAPI" -H 'Content-Type: application/json' -d @payload.json https://noc.social/api/v1/statuses
env:
MASTODONAPI: $
GHTOKEN: $
- name: Build count
uses: masci/datadog@v1
with:
api-key: $
Summary
Today we dug into Dapr Secrets using AKV and GCP Secrets Manager. We showed how to set a value and use them in a container. We then took a pivot to look at Mastodon and how one can post status messages using the REST API. We wrapped up by updating our Github Actions workflow to use the API Token to pull a YAML block and post on our behalf when new Blog entries are released.