Published: Jan 16, 2024 by Isaac Johnson
It’s a been a while since we looked at OpenStatus and Gitness. Have they added any features or functionality since our last looks? We’ll do a quick look at OpenStatus and a much more in-depth review of Gitness as there has been much added since our last writeup.
Openstatus
We last looked at OpenStatus back in October.
I can tell just from comparing our last writeup to now they added SMS to the offerings on the “Pro” plan.
versus today
Status Reports
One of the new features is a “Status Report”.
Click either “Create” button to create the report
It appears this is a way to create a User Notification on an Incident
Clicking confirm indicates it was saved, but stops there
I can now see it listed
The report is viewable here and on our Public URL. Internally:
And on our Public URL: https://fb-status.openstatus.dev/
And I can see a summary of the incident(s) there as well
At any point, we can resolve the incident
And see the status line in the report
I like that it calculates the total time. Often, when doing RCAs at work, we have to build a timetable. One could use this Incident system to keep track of updates along the way.
SMS notifications
I can see, however I cannot use, in the free tier, SMS notifications
Uptime
Since I have had this running now for at least 3mo, I can see any time I was down
This is really handy as a free external monitoring system.
Gitness
Back in October we also looked at Gitness, a new OS GIT offering from Harness.io.
I only checked out the local instance in my Windows box. Let’s try it in Ubuntu proper this time.
In their getting started they mapped to a tmp dir which seems fraut with danger if you really hold GIT code. We’ll use a docker volume instead
$ docker volume create gitnessdata
gitnessdata
$ docker run -d -p 3333:3000 -v /var/run/docker.sock:/var/run/docker.sock -v gitnessdata:/data --name gitness --restart always harness/gitness
Unable to find image 'harness/gitness:latest' locally
latest: Pulling from harness/gitness
96526aa774ef: Already exists
08e4cf3e8a96: Pull complete
0079f8833b55: Pull complete
7c961e087222: Pull complete
adcd91e533d5: Pull complete
7c60e7576997: Pull complete
Digest: sha256:f0da879e02676d93665ddb00252b3d91361a5819e9b7d1396214b99881736849
Status: Downloaded newer image for harness/gitness:latest
e9163bc1f994a26e158e894191df578e88d8e9a3320de22396ac854161dcd725
I can now see it served up on port 3333
I’ll signup for a new account
Now through the UI, there is no way to disable user signups, however I can via docker settings
I’ll reset
$ docker stop gitness
gitness
$ docker rm gitness
gitness
$ docker volume rm gitnessdata
gitnessdata
$ docker volume create gitnessdata
gitnessdata
$ docker run -d -e GITNESS_PRINCIPAL_ADMIN_PASSWORD=MyAdminPassword -e GITNESS_USER_SIGNUP_ENABLED=false -p 3333:3000 -v /var/run/docker.sock:/var/run/docker.sock -v gitnessdata:/data --name gitness --restart always harness/gitness
2dde059b2253882a836f9a9a3c12b3c421cb2e40f99bb8c88cbed0d68d39b7f0
The sign up page still exists, but does not complete
The idea is that I now login as “Admin” with the password I set, then I can create users there
That will create a password for the user at create time
which they can change easily
Let’s create a project
I’ll name it FreshbrewedPublic
Next, I’ll click “New Repository” under Repositories
I’ll make it public with GPLv3
From the Clone, I see it thinks it’s running on the local docker port of 3000, not the exposed 3333
The documentation is still rather absent. but we can see some settings by looking at the code. I believe from this line that what needs to be set is “GITNESS_URL_BASE”
I did a stop and rm, but did not remove the volume (that had data)
$ docker stop gitness
gitness
$ docker rm gitness
gitness
$ docker run -d -e GITNESS_PRINCIPAL_ADMIN_PASSWORD=adfasdfsafasdf -e GITNESS_URL_BASE=http://192.168.1.100:3333/ -e GITNESS_USER_SIGNUP_ENABLED=false -p 3333:3000 -v /var/run/docker.sock:/var/run/docker.sock -v gitnessdata:/data --name gitness --restart always harness/gitness
816c49cb503d7a5f204d4b14daf23b8125cf9d120a122cb9a5096a354677af6b
Now the clone looks right:
We can test the clone
builder@DESKTOP-QADGF36:~/Workspaces$ git clone http://192.168.1.100:3333/git/FreshbrewedPublic/FreshbrewedPublic.git
Cloning into 'FreshbrewedPublic'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 11.16 KiB | 11.16 MiB/s, done.
builder@DESKTOP-QADGF36:~/Workspaces$ cd FreshbrewedPublic/
builder@DESKTOP-QADGF36:~/Workspaces/FreshbrewedPublic$ cat LICENSE | wc -l
234
How might we expose with TLS? I get gunshy with passwords and HTTP without SSL.
First, I need a quick A record
$ cat r53-gitness.json
{
"Comment": "CREATE gitness fb.s A record ",
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "gitness.freshbrewed.science",
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{
"Value": "75.73.224.240"
}
]
}
}
]
}
$ aws route53 change-resource-record-sets --hosted-zone-id Z39E8QFU0F9PZP --change-batch file://r53-gitness.json
{
"ChangeInfo": {
"Id": "/change/C0229976T0QGL2PJV9OO",
"Status": "PENDING",
"SubmittedAt": "2023-12-26T11:58:24.255Z",
"Comment": "CREATE gitness fb.s A record "
}
}
Next, I need to create an external endpoint / external IP entry for this containerized gitness in my primary cluster.
We’ve done this before for Uptime and Rundeck.
As a reminder, we need a service, albeit one without selectors, this matches an “Endpoint” that points to an in-house IP. Lastly, an Ingress handles certs and directing TLS traffic internally through to the endpoint.
$ cat gitness.ingress.yaml
apiVersion: v1
kind: Service
metadata:
name: gitness-external-ip
spec:
ports:
- name: gitnessapp
port: 80
protocol: TCP
targetPort: 3333
clusterIP: None
type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
name: gitness-external-ip
subsets:
- addresses:
- ip: 192.168.1.100
ports:
- name: gitnessapp
port: 3333
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
labels:
app.kubernetes.io/instance: gitness
name: gitnessingress
spec:
rules:
- host: gitness.freshbrewed.science
http:
paths:
- backend:
service:
name: gitness-external-ip
port:
number: 80
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- gitness.freshbrewed.science
secretName: gitness-tls
$ kubectl apply -f ./gitness.ingress.yaml
service/gitness-external-ip created
endpoints/gitness-external-ip created
ingress.networking.k8s.io/gitnessingress created
I had to wait for the cert (slow today)
$ kubectl get cert | tail -n1
gitness-tls True gitness-tls 3m26s
This works, of course:
But to work externally, I also need to let Gitness know it has a new front door:
$ docker stop gitness
gitness
$ docker rm gitness
gitness
$ docker run -d -e GITNESS_PRINCIPAL_ADMIN_PASSWORD=asdfasdfsadfasdf -e GITNESS_URL_BASE=https://gitness.freshbrewed.science/ -e GITNESS_USER_SIGNUP_ENABLED=false -p 3333:3000 -v /var/run/docker.sock:/var/run/docker.sock -v gitnessdata:/data --name gitness --restart always harness/gitness
2bc8660dc4002dfd592bd3895b2751d3a8ddf415b4e5495760029e45e128c92d
We can see this work in action
However, in a fresh InPrivate window I can see even this “Public” repo isn’t insofar as it requires a login
Webhooks
Let’s see what Webhooks can deliver now. I’ll create a new webhook using https://webhook.site/ to test.
I can see I now have a webhook set for all events
I’ll use the inline editor to create a file
I’ll then commit the changes to a new branch
Immediately I saw a payload delivered to Webhook.site:
{
"trigger": "branch_created",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
},
"principal": {
"id": 4,
"uid": "idjohnson",
"display_name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com",
"type": "user",
"created": 1703537228034,
"updated": 1703537325044
},
"ref": {
"name": "refs/heads/mynewbranch",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
}
},
"sha": "652a29168a386d7039de727d1a5f68571b13931a",
"commit": {
"sha": "652a29168a386d7039de727d1a5f68571b13931a",
"message": "Create MyNewFile.md\n\nSome notes about this commit",
"author": {
"identity": {
"name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com"
},
"when": "2023-12-26T12:53:29Z"
},
"committer": {
"identity": {
"name": "Gitness",
"email": "system@gitness.io"
},
"when": "2023-12-26T12:53:29Z"
}
},
"old_sha": "0000000000000000000000000000000000000000",
"forced": false
}
Creating a PR
Delivered
{
"trigger": "pullreq_created",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
},
"principal": {
"id": 4,
"uid": "idjohnson",
"display_name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com",
"type": "user",
"created": 1703537228034,
"updated": 1703537325044
},
"pull_req": {
"number": 1,
"state": "open",
"is_draft": false,
"title": "Create MyNewFile.md",
"source_repo_id": 1,
"source_branch": "mynewbranch",
"target_repo_id": 1,
"target_branch": "main",
"merge_strategy": null,
"author": {
"id": 4,
"uid": "idjohnson",
"display_name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com",
"type": "user",
"created": 1703537228034,
"updated": 1703537325044
}
},
"target_ref": {
"name": "refs/heads/main",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
}
},
"ref": {
"name": "refs/heads/mynewbranch",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
}
},
"sha": "652a29168a386d7039de727d1a5f68571b13931a",
"commit": {
"sha": "652a29168a386d7039de727d1a5f68571b13931a",
"message": "Create MyNewFile.md\n\nSome notes about this commit",
"author": {
"identity": {
"name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com"
},
"when": "2023-12-26T12:53:29Z"
},
"committer": {
"identity": {
"name": "Gitness",
"email": "system@gitness.io"
},
"when": "2023-12-26T12:53:29Z"
}
}
}
Squash and merge
delivered
{
"trigger": "branch_updated",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
},
"principal": {
"id": 4,
"uid": "idjohnson",
"display_name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com",
"type": "user",
"created": 1703537228034,
"updated": 1703537325044
},
"ref": {
"name": "refs/heads/main",
"repo": {
"id": 1,
"path": "FreshbrewedPublic/FreshbrewedPublic",
"uid": "FreshbrewedPublic",
"default_branch": "main",
"git_url": "https://gitness.freshbrewed.science/git/FreshbrewedPublic/FreshbrewedPublic.git"
}
},
"sha": "edb9e4bafcbf54d92eff8e7ebfed30edb4cea997",
"commit": {
"sha": "edb9e4bafcbf54d92eff8e7ebfed30edb4cea997",
"message": "Create MyNewFile.md (#1)",
"author": {
"identity": {
"name": "Isaac Johnson",
"email": "isaac.johnson@gmail.com"
},
"when": "2023-12-26T12:56:48Z"
},
"committer": {
"identity": {
"name": "Gitness",
"email": "system@gitness.io"
},
"when": "2023-12-26T12:56:48Z"
}
},
"old_sha": "259234405c98932716632da8f05d8c3366c49152",
"forced": false
}
I think we get the idea.
We can just use the toggle to disable a webhook
Pipelines
Since the first release, they have added working pipelines.
Let’s create a quick hello-world one
In Pipelines, I’ll click “+ New Pipeline”
Give it a name, which prefills the destination
and we see it created one for us
version: 1
kind: pipeline
spec:
stages:
- name: build
type: ci
spec:
steps:
- name: build
type: run
spec:
container: alpine
script: echo "hello world"
I can now “save and run”. This then prompts for “Run pipeline”
I see it kick in
Interestingly, it got stuck on it’s host URL
This makes me wonder if we really need to set all the values in config.go, even though they say the derive from base.
URL struct {
// Base is used to generate external facing URLs in case they aren't provided explicitly.
// Value is derived from HTTP.Server unless explicitly specified (e.g. http://localhost:3000).
Base string `envconfig:"GITNESS_URL_BASE"`
// Git defines the external URL via which the GIT API is reachable.
// NOTE: for routing to work properly, the request path & hostname reaching gitness
// have to statisfy at least one of the following two conditions:
// - Path ends with `/git`
// - Hostname is different to API hostname
// (this could be after proxy path / header rewrite).
// Value is derived from Base unless explicitly specified (e.g. http://localhost:3000/git).
Git string `envconfig:"GITNESS_URL_GIT"`
// API defines the external URL via which the rest API is reachable.
// NOTE: for routing to work properly, the request path reaching gitness has to end with `/api`
// (this could be after proxy path rewrite).
// Value is derived from Base unless explicitly specified (e.g. http://localhost:3000/api).
API string `envconfig:"GITNESS_URL_API"`
// UI defines the external URL via which the UI is reachable.
// Value is derived from Base unless explicitly specified (e.g. http://localhost:3000).
UI string `envconfig:"GITNESS_URL_UI"`
// Internal defines the internal URL via which the service is reachable.
// Value is derived from HTTP.Server unless explicitly specified (e.g. http://localhost:3000).
Internal string `envconfig:"GITNESS_URL_INTERNAL"`
// Container is the endpoint that can be used by running container builds to communicate
// with gitness (for example while performing a clone on a local repo).
// host.docker.internal allows a running container to talk to services exposed on the host
// (either running directly or via a port exposed in a docker container).
// Value is derived from HTTP.Server unless explicitly specified (e.g. http://host.docker.internal:3000).
Container string `envconfig:"GITNESS_URL_CONTAINER"`
I’ll set all the values
builder@builder-T100:~$ docker stop gitness
gitness
builder@builder-T100:~$ docker rm gitness
gitness
builder@builder-T100:~$ docker run -d -e GITNESS_PRINCIPAL_ADMIN_PASSWORD=asdfasdfasfasfasfasdfsdf -e GITNESS_URL_BASE=https://gitness.freshbrewed.science/ -e GITNESS_URL_GIT=https://gitness.freshbrewed.science/git -e GITNESS_URL_API=https://gitness.freshbrewed.science/api -e GITNESS_URL_UI=https://gitness.freshbrewed.science -e GITNESS_URL_INTERNAL=http://192.168.1.100:3333 -e GITNESS_URL_CONTAINER=http://192.168.1.100:3333 -e GITNESS_USER_SIGNUP_ENABLED=false -p 3333:3000 -v /var/run/docker.sock:/var/run/docker.sock -v gitnessdata:/data --name gitness --restart always harness/gitness
5bcb5bf109cc9563011a55fba6803998363dfabb525a0a67abb6c767cd1825a0
This time it worked like a charm. Clearly, it uses some of the internal URLs
I can see it pulled and used alpine without issue
Secrets
I see there is a section called “Secrets”. Let’s create a secret to see what we can do with it.
I will then edit my pipeline to see if I can fetch the secret
version: 1
kind: pipeline
spec:
stages:
- name: build
type: ci
spec:
steps:
- name: build
type: run
spec:
container: alpine
script: |
echo "hello world"
echo ${{ secrets.get("mysecret") }}
echo ${{ secrets.get("mysecret") }} > t.o
cat t.o | base64
This worked, though clearly it shows the value - I expected some basic masking like I would get in other CI tools
So the short version would be to use “set +x” and/or accept that “secrets” leak in build logs.
Branch rules
It appears they have expanded the Branch Rules we can use to protect a branch
Let’s create a proper branch rule that only the built-in “Admin” can bypass:
I can now see main is protected:
I’ll now try and create against main as my normal user account
Surprisingly that worked without issue
I’ll try with a normal user, perhaps my Gmail is an admin since I “own” that repo. I’ll add Tristan as a contributor
When I try and save an edit as Tristan
I see a “422” error
However, if that user creates a branch for a PR, we are fine
In creating the PR, I see the CI build is run as a check
However, Tristan was able to merge into main without any other stops
I’m wondering if I didn’t include “main” somehow.
I went back to rules and made sure to “+include” main and master branch names
I definitely see them now listed
I tested again as Tristan and clearly the branch rules don’t really stop anything other than direct changes to main. That is a PR is required, but no approvals or checks otherwise.
And just to check, i could bypass with a new file, not just edits. So it clearly isn’t really blocking anything
Summary
We looked at some of the minor tweaks added to OpenStatus since our writeup in October. Really, just some notes on SMS for the paid tier. They did have Incidents back then, but I didn’t explore them in that writeup.
With Gitness, actually quite a lot has changed since our October writeup. Everything from PRs to Webhooks to Branch rules (which clearly do not fully work) have improved. In my first writeup, we did some pipelines but it lacked the plugins and the docs were limited. There is still some improvements needed before I would go all-in, but it’s a positive sign to see it improve as much as it has.