chore: Significantly restructure folder layout
This moves the various projects from "type-based" folders (such as "services" or "tools") into more appropriate semantic folders (such as "nix", "ops" or "web"). Deprecated projects (nixcon-demo & gotest) which only existed for testing/demonstration purposes have been removed. (Note: *all* builds are broken with this commit)
This commit is contained in:
parent
e52eed3cd4
commit
03bfe08e1d
110 changed files with 1 additions and 998 deletions
11
ops/infra/dns/import
Executable file
11
ops/infra/dns/import
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
set -ue
|
||||
|
||||
# Imports a zone file into a Google Cloud DNS zone of the same name
|
||||
readonly ZONE="${1}"
|
||||
|
||||
gcloud dns record-sets import "${ZONE}" \
|
||||
--project composite-watch-759 \
|
||||
--zone-file-format \
|
||||
--delete-all-existing \
|
||||
--zone "${ZONE}"
|
||||
15
ops/infra/dns/kontemplate-works
Normal file
15
ops/infra/dns/kontemplate-works
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
;; -*- mode: zone; -*-
|
||||
;; Do not delete these
|
||||
kontemplate.works. 21600 IN NS ns-cloud-d1.googledomains.com.
|
||||
kontemplate.works. 21600 IN NS ns-cloud-d2.googledomains.com.
|
||||
kontemplate.works. 21600 IN NS ns-cloud-d3.googledomains.com.
|
||||
kontemplate.works. 21600 IN NS ns-cloud-d4.googledomains.com.
|
||||
kontemplate.works. 21600 IN SOA ns-cloud-d1.googledomains.com. cloud-dns-hostmaster.google.com. 4 21600 3600 259200 300
|
||||
|
||||
;; Github site setup
|
||||
kontemplate.works. 60 IN A 185.199.108.153
|
||||
kontemplate.works. 60 IN A 185.199.109.153
|
||||
kontemplate.works. 60 IN A 185.199.110.153
|
||||
kontemplate.works. 60 IN A 185.199.111.153
|
||||
|
||||
www.kontemplate.works. 60 IN CNAME tazjin.github.io.
|
||||
8
ops/infra/dns/oslo-pub
Normal file
8
ops/infra/dns/oslo-pub
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
;; Do not delete these
|
||||
oslo.pub. 21600 IN NS ns-cloud-c1.googledomains.com.
|
||||
oslo.pub. 21600 IN NS ns-cloud-c2.googledomains.com.
|
||||
oslo.pub. 21600 IN NS ns-cloud-c3.googledomains.com.
|
||||
oslo.pub. 21600 IN NS ns-cloud-c4.googledomains.com.
|
||||
oslo.pub. 21600 IN SOA ns-cloud-c1.googledomains.com. cloud-dns-hostmaster.google.com. 4 21600 3600 1209600 300
|
||||
|
||||
oslo.pub. 60 IN A 46.21.106.241
|
||||
33
ops/infra/dns/root-tazj-in
Normal file
33
ops/infra/dns/root-tazj-in
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
;; -*- mode: zone; -*-
|
||||
;; Do not delete these
|
||||
tazj.in. 21600 IN NS ns-cloud-a1.googledomains.com.
|
||||
tazj.in. 21600 IN NS ns-cloud-a2.googledomains.com.
|
||||
tazj.in. 21600 IN NS ns-cloud-a3.googledomains.com.
|
||||
tazj.in. 21600 IN NS ns-cloud-a4.googledomains.com.
|
||||
tazj.in. 21600 IN SOA ns-cloud-a1.googledomains.com. cloud-dns-hostmaster.google.com. 123 21600 3600 1209600 300
|
||||
|
||||
;; Email setup
|
||||
tazj.in. 300 IN MX 1 aspmx.l.google.com.
|
||||
tazj.in. 300 IN MX 5 alt1.aspmx.l.google.com.
|
||||
tazj.in. 300 IN MX 5 alt2.aspmx.l.google.com.
|
||||
tazj.in. 300 IN MX 10 alt3.aspmx.l.google.com.
|
||||
tazj.in. 300 IN MX 10 alt4.aspmx.l.google.com.
|
||||
tazj.in. 300 IN TXT "v=spf1 include:_spf.google.com ~all"
|
||||
google._domainkey.tazj.in. 21600 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9AphX/WJf8zVXQB5Jk0Ry1MI6ARa6vEyAoJtpjpt9Nbm7XU4qVWFRJm+L0VFd5EZ5YDPJTIZ90lJE3/B8vae2ipnoGbJbj8LaVSzzIPMbWmhPhX3fkLJFdkv7xRDMDn730iYXRlfkgv6GsqbS8vZt7mzxx4mpnePTI323yjRVkwRW8nGVbsmB25ZoG1/0985" "kg4mSYxzWeJ2ozCPFhT4sfMtZMXe/4QEkJz/zkod29KZfFJmLgEaf73WLdBX8kdwbhuh2PYXt/PwzUrRzF5ujVCsSaTZwdRVPErcf+yo4NvedelTjjs8rFVfoJiaDD1q2bQ3w0gDEBWPdC2VP7k9zwIDAQAB"
|
||||
|
||||
;; Site verifications
|
||||
tazj.in. 3600 IN TXT "keybase-site-verification=gC4kzEmnLzY7F669PjN-pw2Cf__xHqcxQ08Gb-W9dhE"
|
||||
tazj.in. 300 IN TXT "google-site-verification=d3_MI1OwD6q2OT42Vvh0I9w2u3Q5KFBu-PieNUE1Fig"
|
||||
www.tazj.in. 3600 IN TXT "keybase-site-verification=ER8m_byyqAhzeIy9TyzkAU1H2p2yHtpvImuB_XrRF2U"
|
||||
|
||||
;; Blog "storage engine"
|
||||
blog.tazj.in. 21600 IN NS ns-cloud-c1.googledomains.com.
|
||||
blog.tazj.in. 21600 IN NS ns-cloud-c2.googledomains.com.
|
||||
blog.tazj.in. 21600 IN NS ns-cloud-c3.googledomains.com.
|
||||
blog.tazj.in. 21600 IN NS ns-cloud-c4.googledomains.com.
|
||||
|
||||
;; Webpage records setup
|
||||
tazj.in. 300 IN A 34.98.120.189
|
||||
www.tazj.in. 300 IN A 34.98.120.189
|
||||
git.tazj.in. 300 IN A 34.98.120.189
|
||||
files.tazj.in. 300 IN CNAME c.storage.googleapis.com.
|
||||
3
ops/infra/gcp/.gitignore
vendored
Normal file
3
ops/infra/gcp/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.terraform
|
||||
*.tfstate
|
||||
*.tfstate.backup
|
||||
111
ops/infra/gcp/default.tf
Normal file
111
ops/infra/gcp/default.tf
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# Terraform configuration for the GCP project 'tazjins-infrastructure'
|
||||
|
||||
provider "google" {
|
||||
project = "tazjins-infrastructure"
|
||||
region = "europe-north1"
|
||||
}
|
||||
|
||||
# Configure a storage bucket in which to keep Terraform state and
|
||||
# other data, such as Nixery's layers.
|
||||
resource "google_storage_bucket" "tazjins-data" {
|
||||
name = "tazjins-data"
|
||||
location = "EU"
|
||||
}
|
||||
|
||||
terraform {
|
||||
backend "gcs" {
|
||||
bucket = "tazjins-data"
|
||||
prefix = "terraform"
|
||||
}
|
||||
}
|
||||
|
||||
# Configure enabled APIs
|
||||
resource "google_project_services" "primary" {
|
||||
project = "tazjins-infrastructure"
|
||||
services = [
|
||||
"bigquery-json.googleapis.com",
|
||||
"bigquerystorage.googleapis.com",
|
||||
"cloudapis.googleapis.com",
|
||||
"clouddebugger.googleapis.com",
|
||||
"cloudfunctions.googleapis.com",
|
||||
"cloudkms.googleapis.com",
|
||||
"cloudtrace.googleapis.com",
|
||||
"compute.googleapis.com",
|
||||
"container.googleapis.com",
|
||||
"containerregistry.googleapis.com",
|
||||
"datastore.googleapis.com",
|
||||
"dns.googleapis.com",
|
||||
"iam.googleapis.com",
|
||||
"iamcredentials.googleapis.com",
|
||||
"logging.googleapis.com",
|
||||
"monitoring.googleapis.com",
|
||||
"oslogin.googleapis.com",
|
||||
"pubsub.googleapis.com",
|
||||
"run.googleapis.com",
|
||||
"servicemanagement.googleapis.com",
|
||||
"serviceusage.googleapis.com",
|
||||
"sourcerepo.googleapis.com",
|
||||
"sql-component.googleapis.com",
|
||||
"storage-api.googleapis.com",
|
||||
"storage-component.googleapis.com",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# Configure the main Kubernetes cluster in which services are deployed
|
||||
resource "google_container_cluster" "primary" {
|
||||
name = "tazjin-cluster"
|
||||
location = "europe-north1"
|
||||
|
||||
remove_default_node_pool = true
|
||||
initial_node_count = 1
|
||||
}
|
||||
|
||||
resource "google_container_node_pool" "primary_nodes" {
|
||||
name = "primary-nodes"
|
||||
location = "europe-north1"
|
||||
cluster = google_container_cluster.primary.name
|
||||
node_count = 1
|
||||
|
||||
node_config {
|
||||
preemptible = true
|
||||
machine_type = "n1-standard-2"
|
||||
|
||||
oauth_scopes = [
|
||||
"storage-rw",
|
||||
"logging-write",
|
||||
"monitoring",
|
||||
"https://www.googleapis.com/auth/source.read_only",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# Configure a service account for which GCS URL signing keys can be created.
|
||||
resource "google_service_account" "nixery" {
|
||||
account_id = "nixery"
|
||||
display_name = "Nixery service account"
|
||||
}
|
||||
|
||||
# Configure Cloud KMS for secret encryption
|
||||
resource "google_kms_key_ring" "tazjins_keys" {
|
||||
name = "tazjins-keys"
|
||||
location = "europe-north1"
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_kms_crypto_key" "kontemplate_key" {
|
||||
name = "kontemplate-key"
|
||||
key_ring = google_kms_key_ring.tazjins_keys.id
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
# Configure the git repository that contains everything.
|
||||
resource "google_sourcerepo_repository" "depot" {
|
||||
name = "depot"
|
||||
}
|
||||
73
ops/infra/kubernetes/cgit/config.yaml
Normal file
73
ops/infra/kubernetes/cgit/config.yaml
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gcsr-secrets
|
||||
type: Opaque
|
||||
data:
|
||||
username: "Z2l0LXRhemppbi5nbWFpbC5jb20="
|
||||
# This credential is a GCSR 'gitcookie' token.
|
||||
password: '{{ passLookup "gcsr-tazjin-password" | b64enc }}'
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cgit
|
||||
labels:
|
||||
app: cgit
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cgit
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cgit
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: cgit
|
||||
image: nixery.local/shell/services.cgit-taz:{{ gitHEAD }}
|
||||
command: [ "cgit-launch" ]
|
||||
env:
|
||||
- name: HOME
|
||||
value: /git
|
||||
volumeMounts:
|
||||
- name: git-volume
|
||||
mountPath: /git
|
||||
- name: sync-gcsr
|
||||
image: nixery.local/shell/services.sync-gcsr:{{ gitHEAD }}
|
||||
command: [ "sync-gcsr" ]
|
||||
env:
|
||||
- name: SYNC_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gcsr-secrets
|
||||
key: username
|
||||
- name: SYNC_PASS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gcsr-secrets
|
||||
key: password
|
||||
volumeMounts:
|
||||
- name: git-volume
|
||||
mountPath: /git
|
||||
volumes:
|
||||
- name: git-volume
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cgit
|
||||
spec:
|
||||
selector:
|
||||
app: cgit
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
19
ops/infra/kubernetes/gemma/config.lisp
Normal file
19
ops/infra/kubernetes/gemma/config.lisp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
(config :port 4242
|
||||
:data-dir "/var/lib/gemma/")
|
||||
|
||||
(deftask bathroom/wipe-mirror 7)
|
||||
(deftask bathroom/wipe-counter 7)
|
||||
|
||||
;; Bedroom tasks
|
||||
(deftask bedroom/change-sheets 7)
|
||||
(deftask bedroom/vacuum 10)
|
||||
|
||||
;; Kitchen tasks
|
||||
(deftask kitchen/normal-trash 3)
|
||||
(deftask kitchen/green-trash 5)
|
||||
(deftask kitchen/blue-trash 5)
|
||||
(deftask kitchen/wipe-counters 3)
|
||||
(deftask kitchen/vacuum 5 "Kitchen has more crumbs and such!")
|
||||
|
||||
;; Entire place
|
||||
(deftask clean-windows 60)
|
||||
8
ops/infra/kubernetes/https-cert/cert.yaml
Normal file
8
ops/infra/kubernetes/https-cert/cert.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
apiVersion: networking.gke.io/v1beta1
|
||||
kind: ManagedCertificate
|
||||
metadata:
|
||||
name: {{ .domain | replace "." "-" }}
|
||||
spec:
|
||||
domains:
|
||||
- {{ .domain }}
|
||||
35
ops/infra/kubernetes/https-lb/ingress.yaml
Normal file
35
ops/infra/kubernetes/https-lb/ingress.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# This resource configures the HTTPS load balancer that is used as the
|
||||
# entrypoint to all HTTPS services running in the cluster.
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: https-ingress
|
||||
annotations:
|
||||
networking.gke.io/managed-certificates: tazj-in, git-tazj-in, www-tazj-in, oslo-pub
|
||||
spec:
|
||||
rules:
|
||||
# Route blog to the blog ...
|
||||
- host: tazj.in
|
||||
http:
|
||||
paths:
|
||||
- path: /*
|
||||
backend:
|
||||
serviceName: tazblog
|
||||
servicePort: 8000
|
||||
# Route git.tazj.in to the cgit pods
|
||||
- host: git.tazj.in
|
||||
http:
|
||||
paths:
|
||||
- path: /*
|
||||
backend:
|
||||
serviceName: nginx
|
||||
servicePort: 6756
|
||||
# Route oslo.pub to the nginx instance which serves redirects
|
||||
- host: oslo.pub
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: nginx
|
||||
servicePort: 6756
|
||||
59
ops/infra/kubernetes/nginx/nginx.conf
Normal file
59
ops/infra/kubernetes/nginx/nginx.conf
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
daemon off;
|
||||
worker_processes 1;
|
||||
error_log stderr;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
log_format json_combined escape=json
|
||||
'{'
|
||||
'"time_local":"$time_local",'
|
||||
'"remote_addr":"$remote_addr",'
|
||||
'"remote_user":"$remote_user",'
|
||||
'"request":"$request",'
|
||||
'"status": "$status",'
|
||||
'"body_bytes_sent":"$body_bytes_sent",'
|
||||
'"request_time":"$request_time",'
|
||||
'"http_referrer":"$http_referer",'
|
||||
'"http_user_agent":"$http_user_agent"'
|
||||
'}';
|
||||
|
||||
access_log /dev/stdout json_combined;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
location / {
|
||||
return 200 "ok";
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name oslo.pub;
|
||||
|
||||
location / {
|
||||
return 302 https://www.google.com/maps/d/viewer?mid=1pJIYY9cuEdt9DuMTbb4etBVq7hs;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name git.tazj.in;
|
||||
|
||||
# Static assets must always hit the root.
|
||||
location ~ ^/(favicon\.ico|cgit\.(css|png))$ {
|
||||
proxy_pass http://cgit;
|
||||
}
|
||||
|
||||
# Everything else hits the depot directly.
|
||||
location / {
|
||||
proxy_pass http://cgit/cgit.cgi/depot/;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
ops/infra/kubernetes/nginx/nginx.yaml
Normal file
60
ops/infra/kubernetes/nginx/nginx.yaml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Deploy an nginx instance which serves ... redirects.
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: nginx-conf
|
||||
data:
|
||||
nginx.conf: {{ insertFile "nginx.conf" | toJson }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
config: {{ insertFile "nginx.conf" | sha1sum }}
|
||||
spec:
|
||||
containers:
|
||||
- name: tazblog
|
||||
image: nixery.local/shell/third_party.nginx:{{ .version }}
|
||||
command: ["/bin/bash", "-c"]
|
||||
args:
|
||||
- |
|
||||
cd /run
|
||||
echo 'nogroup:x:30000:nobody' >> /etc/group
|
||||
echo 'nobody:x:30000:30000:nobody:/tmp:/bin/bash' >> /etc/passwd
|
||||
exec nginx -c /etc/nginx/nginx.conf
|
||||
volumeMounts:
|
||||
- name: nginx-conf
|
||||
mountPath: /etc/nginx
|
||||
- name: nginx-rundir
|
||||
mountPath: /run
|
||||
volumes:
|
||||
- name: nginx-conf
|
||||
configMap:
|
||||
name: nginx-conf
|
||||
- name: nginx-rundir
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: nginx
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 6756
|
||||
targetPort: 80
|
||||
67
ops/infra/kubernetes/nixery/config.yaml
Normal file
67
ops/infra/kubernetes/nixery/config.yaml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Deploys an instance of Nixery into the cluster.
|
||||
#
|
||||
# The service via which Nixery is exposed has a private DNS entry
|
||||
# pointing to it, which makes it possible to resolve `nixery.local`
|
||||
# in-cluster without things getting nasty.
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nixery
|
||||
namespace: kube-public
|
||||
labels:
|
||||
app: nixery
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nixery
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nixery
|
||||
spec:
|
||||
containers:
|
||||
- name: nixery
|
||||
image: eu.gcr.io/tazjins-infrastructure/nixery:{{ .version }}
|
||||
volumeMounts:
|
||||
- name: nixery-secrets
|
||||
mountPath: /var/nixery
|
||||
env:
|
||||
- name: BUCKET
|
||||
value: {{ .bucket}}
|
||||
- name: PORT
|
||||
value: "{{ .port }}"
|
||||
- name: GOOGLE_APPLICATION_CREDENTIALS
|
||||
value: /var/nixery/gcs-key.json
|
||||
- name: GCS_SIGNING_KEY
|
||||
value: /var/nixery/gcs-key.pem
|
||||
- name: GCS_SIGNING_ACCOUNT
|
||||
value: {{ .account }}
|
||||
- name: GIT_SSH_COMMAND
|
||||
value: 'ssh -F /var/nixery/ssh_config'
|
||||
- name: NIXERY_PKGS_REPO
|
||||
value: {{ .repo }}
|
||||
- name: NIX_POPULARITY_URL
|
||||
value: 'https://storage.googleapis.com/nixery-layers/popularity/{{ .popularity }}'
|
||||
volumes:
|
||||
- name: nixery-secrets
|
||||
secret:
|
||||
secretName: nixery-secrets
|
||||
defaultMode: 256
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nixery
|
||||
namespace: kube-public
|
||||
annotations:
|
||||
cloud.google.com/load-balancer-type: "Internal"
|
||||
spec:
|
||||
selector:
|
||||
app: nixery
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
1
ops/infra/kubernetes/nixery/id_nixery.pub
Normal file
1
ops/infra/kubernetes/nixery/id_nixery.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzBM6ydst77jDHNcTFWKD9Fw4SReqyNEEp2MtQBk2wt94U4yLp8MQIuNeOEn1GaDEX4RGCxqai/2UVF1w9ZNdU+v2fXcKWfkKuGQH2XcNfXor2cVNObd40H78++iZiv3nmM/NaEdkTbTBbi925cRy9u5FgItDgsJlyKNRglCb0fr6KlgpvWjL20dp/eeZ8a/gLniHK8PnEsgERQSvJnsyFpxxVhxtoUiyLWpXDl4npf/rQr0eRDf4Q5sN/nbTwksapPHfze8dKcaoA7A2NqT3bJ6DPGrwVCzGRtGw/SXJwFwmmtAl9O6BklpeReyiknSxc+KOtrjDW6O0r6yvymD5Z nixery
|
||||
2
ops/infra/kubernetes/nixery/known_hosts
Normal file
2
ops/infra/kubernetes/nixery/known_hosts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
||||
140.82.118.4 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
||||
18
ops/infra/kubernetes/nixery/secrets.yaml
Normal file
18
ops/infra/kubernetes/nixery/secrets.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# The secrets below are encrypted using keys stored in Cloud KMS and
|
||||
# templated in by kontemplate when deploying.
|
||||
#
|
||||
# Not all of the values are actually secret (see the matching)
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: nixery-secrets
|
||||
namespace: kube-public
|
||||
type: Opaque
|
||||
data:
|
||||
gcs-key.json: {{ passLookup "nixery-gcs-json" | b64enc }}
|
||||
gcs-key.pem: {{ passLookup "nixery-gcs-pem" | b64enc }}
|
||||
id_nixery: {{ printf "%s\n" (passLookup "nixery-ssh-private") | b64enc }}
|
||||
id_nixery.pub: {{ insertFile "id_nixery.pub" | b64enc }}
|
||||
known_hosts: {{ insertFile "known_hosts" | b64enc }}
|
||||
ssh_config: {{ insertFile "ssh_config" | b64enc }}
|
||||
4
ops/infra/kubernetes/nixery/ssh_config
Normal file
4
ops/infra/kubernetes/nixery/ssh_config
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Match host *
|
||||
User tazjin@google.com
|
||||
IdentityFile /var/nixery/id_nixery
|
||||
UserKnownHostsFile /var/nixery/known_hosts
|
||||
38
ops/infra/kubernetes/primary-cluster.yaml
Normal file
38
ops/infra/kubernetes/primary-cluster.yaml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Kontemplate configuration for the primary GKE cluster in the project
|
||||
# 'tazjins-infrastructure'.
|
||||
---
|
||||
context: gke_tazjins-infrastructure_europe-north1_tazjin-cluster
|
||||
include:
|
||||
# SSL certificates (provisioned by Google)
|
||||
- name: tazj-in-cert
|
||||
path: https-cert
|
||||
values:
|
||||
domain: tazj.in
|
||||
- name: www-tazj-in-cert
|
||||
path: https-cert
|
||||
values:
|
||||
domain: www.tazj.in
|
||||
- name: git-tazj-in-cert
|
||||
path: https-cert
|
||||
values:
|
||||
domain: git.tazj.in
|
||||
- name: oslo-pub-cert
|
||||
path: https-cert
|
||||
values:
|
||||
domain: oslo.pub
|
||||
|
||||
# Services
|
||||
- name: nixery
|
||||
values:
|
||||
port: 8080
|
||||
version: xkm36vrbcnzxdccybzdrx4qzfcfqfrhg
|
||||
bucket: tazjins-data
|
||||
account: nixery@tazjins-infrastructure.iam.gserviceaccount.com
|
||||
repo: ssh://tazjin@gmail.com@source.developers.google.com:2022/p/tazjins-infrastructure/r/depot
|
||||
popularity: 'popularity-nixos-unstable-3140fa89c51233397f496f49014f6b23216667c2.json'
|
||||
- name: tazblog
|
||||
- name: cgit
|
||||
- name: https-lb
|
||||
- name: nginx
|
||||
values:
|
||||
version: a349d5e9145ae9a6c89f62ec631f01fb180de546
|
||||
34
ops/infra/kubernetes/tazblog/config.yaml
Normal file
34
ops/infra/kubernetes/tazblog/config.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: tazblog
|
||||
labels:
|
||||
app: tazblog
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: tazblog
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: tazblog
|
||||
spec:
|
||||
containers:
|
||||
- name: tazblog
|
||||
image: nixery.local/shell/services.tazblog:{{ gitHEAD }}
|
||||
command: [ "tazblog" ]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tazblog
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: tazblog
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
3
ops/infra/nixos/.gitignore
vendored
Normal file
3
ops/infra/nixos/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
hardware-configuration.nix
|
||||
local-configuration.nix
|
||||
result
|
||||
23
ops/infra/nixos/README.md
Normal file
23
ops/infra/nixos/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
NixOS configuration
|
||||
===================
|
||||
|
||||
My NixOS configuration! It configures most of the packages I require
|
||||
on my systems, sets up Emacs the way I need and does a bunch of other
|
||||
interesting things.
|
||||
|
||||
In contrast with earlier versions of this configuration, the Nix
|
||||
channel versions are now pinned in Nix (see the beginning of
|
||||
[packages.nix][]).
|
||||
|
||||
Machine-local configuration is kept in files with the naming scheme
|
||||
`$hostname-configuration.nix` and **must** be symlinked to
|
||||
`local-configuration.nix` before the first configuration run.
|
||||
|
||||
I'm publishing this repository (and my [emacs configuration][]) as a
|
||||
convenience for myself, but also as a resource that people looking for
|
||||
example Nix or Emacs configurations can browse through.
|
||||
|
||||
Feel free to ping me with any questions you might have.
|
||||
|
||||
[packages.nix]: packages.nix
|
||||
[emacs configuration]: https://github.com/tazjin/emacs.d
|
||||
200
ops/infra/nixos/adho-configuration.nix
Normal file
200
ops/infra/nixos/adho-configuration.nix
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# Local configuration for 'adho' (Thinkpad T470s)
|
||||
{ config, pkgs, ...}:
|
||||
|
||||
{
|
||||
boot.initrd.luks.devices.adho.device = "/dev/disk/by-uuid/722006b0-9654-4ea1-8703-e0cf9ac1905e";
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
|
||||
services.xserver.libinput.enable = true;
|
||||
services.xserver.videoDrivers = [ "intel" ];
|
||||
programs.light.enable = true;
|
||||
|
||||
# Office printer configuration
|
||||
services.printing.enable = true;
|
||||
services.printing.drivers = [ pkgs.hplip ];
|
||||
services.avahi.enable = true;
|
||||
services.avahi.nssmdns = true;
|
||||
|
||||
# Enable VirtualBox to update Beatstep Pro firmware:
|
||||
virtualisation.virtualbox.host.enable = true;
|
||||
virtualisation.virtualbox.host.enableExtensionPack = true;
|
||||
|
||||
# Enable LXC/LXD for Nixini work
|
||||
virtualisation.lxd.enable = true;
|
||||
|
||||
# Give me more entropy:
|
||||
services.haveged.enable = true;
|
||||
|
||||
# Disable sandbox to let work-builds function:
|
||||
nix.useSandbox = false;
|
||||
|
||||
# Yubikey related:
|
||||
services.pcscd.enable = true;
|
||||
environment.systemPackages = with pkgs; [
|
||||
cfssl
|
||||
libp11
|
||||
opensc
|
||||
yubico-piv-tool
|
||||
];
|
||||
|
||||
networking = {
|
||||
hostName = "adho";
|
||||
wireless.enable = true;
|
||||
wireless.userControlled.enable = true;
|
||||
|
||||
wireless.networks = {
|
||||
# Welcome to roast club!
|
||||
"How do I computer?" = {
|
||||
psk = "washyourface";
|
||||
};
|
||||
|
||||
# On the go!
|
||||
"Rumpetroll" = {
|
||||
psk = "fisk1234";
|
||||
# If this network exists, chances are that I want it:
|
||||
priority = 10;
|
||||
};
|
||||
|
||||
# Public places in Oslo:
|
||||
"Abelone" = {
|
||||
psk = "speakeasy";
|
||||
};
|
||||
|
||||
"Wurst" = {
|
||||
psk = "wurst2015";
|
||||
};
|
||||
|
||||
"postkontoret" = {
|
||||
psk = "postkontoret";
|
||||
};
|
||||
|
||||
# Eugene's apartment:
|
||||
"GET_5G_4FD250" = {
|
||||
psk = "62636342";
|
||||
};
|
||||
|
||||
# FSCONS 2017
|
||||
"uioguest" = {};
|
||||
|
||||
# Hackeriet!
|
||||
"hackeriet.no" = {
|
||||
psk = "hackeriet.no";
|
||||
};
|
||||
|
||||
# Cafe Sara
|
||||
"Sara Nett" = {
|
||||
psk = "sarabar1989";
|
||||
};
|
||||
|
||||
# The Dubliner
|
||||
"DubGjest" = {
|
||||
# of course
|
||||
psk = "Guinness";
|
||||
};
|
||||
|
||||
"MAGNAT Guest" = {
|
||||
psk = "elmolino021";
|
||||
};
|
||||
|
||||
"BrewDog" = {
|
||||
psk = "welovebeer";
|
||||
};
|
||||
|
||||
# Dima's
|
||||
"What's a Bad Idea?" = {
|
||||
psk = "DQDxzrzIvy0YtDwH";
|
||||
};
|
||||
|
||||
# Loke's
|
||||
"VMC28F76E" = {
|
||||
psk = "d2ftQnr6xppw";
|
||||
};
|
||||
|
||||
"SafetyWiFi - Teknologihuset" = {
|
||||
psk = "tech4ever";
|
||||
};
|
||||
|
||||
"Selvaag Pluss" = {
|
||||
psk = "detlilleekstra";
|
||||
};
|
||||
|
||||
"Langler" = {
|
||||
psk = "Oslo2018";
|
||||
};
|
||||
|
||||
# Pils & Programmering
|
||||
"BEKKguest" = {
|
||||
psk = "guest7890";
|
||||
};
|
||||
|
||||
"Homan-Gjest" = {
|
||||
psk = "haveaniceday";
|
||||
};
|
||||
|
||||
# Røverstaden
|
||||
"Roverstaden" = {
|
||||
psk = "r0verstaden2018";
|
||||
};
|
||||
|
||||
"The Brew Dock" = {
|
||||
psk = "realbeer";
|
||||
};
|
||||
|
||||
"econ-guest" = {
|
||||
psk = "Finance2010";
|
||||
};
|
||||
|
||||
"KabelBox-2FD0" = {
|
||||
psk = "92433048597489095671";
|
||||
};
|
||||
|
||||
"TheKasbah" = {
|
||||
psk = "couscous";
|
||||
};
|
||||
|
||||
# Kitty's misspelled network.
|
||||
"How do I Computer?" = {
|
||||
psk = "herpderpponies";
|
||||
};
|
||||
|
||||
# NixCon 2018
|
||||
"Coin Street Community Builders " = {
|
||||
psk = "3vents2016";
|
||||
};
|
||||
|
||||
"KH2 Gjest" = {
|
||||
psk = "haenfindag";
|
||||
};
|
||||
|
||||
# Forest & Brown
|
||||
"Forest Guest" = {
|
||||
psk = "437B99AC5B";
|
||||
};
|
||||
|
||||
"Gatwick FREE Wi-Fi" = {};
|
||||
"mycloud" = {};
|
||||
"Norwegian Internet Access" = {};
|
||||
"NSB_INTERAKTIV" = {};
|
||||
"The Thief" = {};
|
||||
"espressohouse" = {};
|
||||
"Gotanet Open" = {};
|
||||
"wifi.flytoget.no" = {};
|
||||
"AIRPORT" = {};
|
||||
"ilcaffelovesyou" = {};
|
||||
"WIFIonICE" = {};
|
||||
"Lorry Gjest" = {};
|
||||
"Amundsengjest" = {};
|
||||
"Beer Palace Gjest" = {};
|
||||
"ibis" = {};
|
||||
"GoogleGuest" = {};
|
||||
};
|
||||
};
|
||||
|
||||
hardware.bluetooth.enable = true;
|
||||
|
||||
# Configure POSIX queue limits (for work)
|
||||
systemd.tmpfiles.rules = let mqueue = "/proc/sys/fs/mqueue"; in [
|
||||
"w ${mqueue}/msgsize_max - - - - ${toString (64 * 1024)}"
|
||||
"w ${mqueue}/msg_max - - - - 50"
|
||||
];
|
||||
}
|
||||
102
ops/infra/nixos/configuration.nix
Normal file
102
ops/infra/nixos/configuration.nix
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Edit this configuration file to define what should be installed on
|
||||
# your system. Help is available in the configuration.nix(5) man page
|
||||
# and in the NixOS manual (accessible by running ‘nixos-help’).
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[
|
||||
./desktop.nix
|
||||
./dotfiles.nix
|
||||
./hardware-configuration.nix
|
||||
./local-configuration.nix
|
||||
./mail.nix
|
||||
./packages.nix
|
||||
];
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.cleanTmpDir = true;
|
||||
hardware.pulseaudio.enable = true;
|
||||
time.timeZone = "Europe/Oslo";
|
||||
|
||||
# Configure audio setup for JACK + Overtone
|
||||
boot.kernelModules = [ "snd-seq" "snd-rawmidi" ];
|
||||
hardware.pulseaudio.package = pkgs.pulseaudioFull;
|
||||
|
||||
# Update Intel microcode on boot (both machines have Intel CPUs):
|
||||
hardware.cpu.intel.updateMicrocode = true;
|
||||
|
||||
networking = {
|
||||
# Don't use ISP's DNS servers:
|
||||
nameservers = [
|
||||
"1.1.1.1"
|
||||
"1.0.0.1"
|
||||
];
|
||||
|
||||
# Open Chromecast-related ports & servedir
|
||||
firewall.allowedTCPPorts = [ 3000 5556 5558 ];
|
||||
};
|
||||
|
||||
# Generate an immutable /etc/resolv.conf from the nameserver settings
|
||||
# above (otherwise DHCP overwrites it):
|
||||
environment.etc."resolv.conf" = with lib; with pkgs; {
|
||||
source = writeText "resolv.conf" ''
|
||||
${concatStringsSep "\n" (map (ns: "nameserver ${ns}") config.networking.nameservers)}
|
||||
options edns0
|
||||
'';
|
||||
};
|
||||
|
||||
# Configure emacs:
|
||||
# (actually, that's a lie, this only installs emacs!)
|
||||
services.emacs = {
|
||||
install = true;
|
||||
defaultEditor = true;
|
||||
package = import ./emacs.nix { inherit pkgs; };
|
||||
};
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
# Enable GNOME keyring (required for Evolution)
|
||||
services.gnome3.gnome-keyring.enable = true;
|
||||
|
||||
virtualisation = {
|
||||
# Configure Docker (with socket activation):
|
||||
# Side note: ... why is this in virtualisation? ...
|
||||
docker.enable = true;
|
||||
docker.autoPrune.enable = true;
|
||||
};
|
||||
|
||||
# Configure various other applications:
|
||||
programs = {
|
||||
java.enable = true;
|
||||
java.package = pkgs.openjdk;
|
||||
|
||||
fish.enable = true;
|
||||
ssh.startAgent = true;
|
||||
};
|
||||
|
||||
services.postgresql.enable = true;
|
||||
|
||||
# Configure user account
|
||||
users.defaultUserShell = pkgs.fish;
|
||||
users.extraUsers.vincent = {
|
||||
extraGroups = [ "wheel" "docker" "vboxusers" "lxd" ];
|
||||
isNormalUser = true;
|
||||
uid = 1000;
|
||||
shell = pkgs.fish;
|
||||
};
|
||||
|
||||
security.sudo = {
|
||||
enable = true;
|
||||
extraConfig = "wheel ALL=(ALL:ALL) SETENV: ALL";
|
||||
};
|
||||
|
||||
# This value determines the NixOS release with which your system is to be
|
||||
# compatible, in order to avoid breaking some software such as database
|
||||
# servers. You should change this only after NixOS release notes say you
|
||||
# should.
|
||||
system.stateVersion = "18.03"; # Did you read the comment?
|
||||
}
|
||||
6
ops/infra/nixos/default.nix
Normal file
6
ops/infra/nixos/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{ ... }:
|
||||
|
||||
builtins.throw ''
|
||||
The Nix derivations at infra/nixos are not meant to be evaluated
|
||||
like a derivation as they represent NixOS configuration.
|
||||
''
|
||||
82
ops/infra/nixos/desktop.nix
Normal file
82
ops/infra/nixos/desktop.nix
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# Configuration for the desktop environment
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let emacs = import ./emacs.nix { inherit pkgs; };
|
||||
screenLock = pkgs.writeShellScriptBin "screen-lock" ''
|
||||
find ${pkgs.wallpapers} -name "*.png" | shuf -n1 | xargs i3lock -f -t -i
|
||||
'';
|
||||
in {
|
||||
# Configure basic X-server stuff:
|
||||
services.xserver = {
|
||||
enable = true;
|
||||
layout = "us,no";
|
||||
xkbOptions = "caps:super, grp:shifts_toggle, parens:swap_brackets";
|
||||
exportConfiguration = true;
|
||||
|
||||
# Give EXWM permission to control the session.
|
||||
displayManager.sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
|
||||
|
||||
# Use the pre 18.09 default display manager (slim)
|
||||
displayManager.slim.enable = true;
|
||||
};
|
||||
|
||||
# Add a shell script with random screen lock wallpaper selection
|
||||
environment.systemPackages = [ screenLock ];
|
||||
|
||||
# Apparently when you have house guests they complain about your screen tearing!
|
||||
services.compton.enable = true;
|
||||
services.compton.backend = "xrender";
|
||||
|
||||
# Configure desktop environment:
|
||||
services.xserver.windowManager.session = lib.singleton {
|
||||
name = "exwm";
|
||||
start = ''
|
||||
${emacs}/bin/emacs --eval '(progn (server-start) (exwm-enable))'
|
||||
'';
|
||||
};
|
||||
|
||||
# Configure Redshift for Oslo
|
||||
services.redshift = {
|
||||
enable = true;
|
||||
latitude = "59.911491";
|
||||
longitude = "10.757933";
|
||||
};
|
||||
|
||||
# Configure fonts
|
||||
fonts = {
|
||||
fonts = with pkgs; [
|
||||
corefonts
|
||||
font-awesome-ttf
|
||||
input-fonts
|
||||
noto-fonts-cjk
|
||||
noto-fonts-emoji
|
||||
powerline-fonts
|
||||
helvetica-neue-lt-std
|
||||
];
|
||||
};
|
||||
|
||||
# Configure random setting of wallpapers
|
||||
systemd.user.services.feh-wp = {
|
||||
description = "Randomly set wallpaper via feh";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
WorkingDirectory = "${pkgs.wallpapers}/share/wallpapers";
|
||||
|
||||
# Manually shuffle because feh's --randomize option can't be restricted to
|
||||
# just certain file types.
|
||||
ExecStart = "${pkgs.bash}/bin/bash -c '${pkgs.fd}/bin/fd -atf | shuf | head -n1 | ${pkgs.findutils}/bin/xargs ${pkgs.feh}/bin/feh --bg-fill'";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.timers.feh-wp = {
|
||||
description = "Set a random wallpaper every hour";
|
||||
wantedBy = [ "graphical-session.target" ];
|
||||
partOf = [ "graphical-session.target" ];
|
||||
|
||||
timerConfig = {
|
||||
OnActiveSec = "1second";
|
||||
OnUnitActiveSec = "1hour";
|
||||
};
|
||||
};
|
||||
}
|
||||
27
ops/infra/nixos/dotfiles.nix
Normal file
27
ops/infra/nixos/dotfiles.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Bundle configuration files into a derivation.
|
||||
# I call this derivation dotfiles despite that not technically being true
|
||||
# anymore ...
|
||||
|
||||
{ config, pkgs, ...}:
|
||||
|
||||
let dotfiles = pkgs.stdenv.mkDerivation {
|
||||
name = "tazjins-dotfiles";
|
||||
|
||||
srcs = [
|
||||
./dotfiles
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp ./* $out/
|
||||
'';
|
||||
};
|
||||
in {
|
||||
# /etc/ is a special place in NixOS!
|
||||
# Symlinks that need to be created there must be specified explicitly.
|
||||
environment.etc = {
|
||||
"alacritty.yml".source = "${dotfiles}/alacritty.yml";
|
||||
"fish/config.fish".source = "${dotfiles}/config.fish";
|
||||
"tmux.conf".source = "${dotfiles}/tmux.conf";
|
||||
};
|
||||
}
|
||||
203
ops/infra/nixos/dotfiles/alacritty.yml
Normal file
203
ops/infra/nixos/dotfiles/alacritty.yml
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
# Configuration for Alacritty, the GPU enhanced terminal emulator
|
||||
|
||||
# Any items in the `env` entry below will be added as
|
||||
# environment variables. Some entries may override variables
|
||||
# set by alacritty it self.
|
||||
env:
|
||||
TERM: xterm-256color
|
||||
|
||||
window:
|
||||
# TODO
|
||||
decorations: full
|
||||
|
||||
scrolling:
|
||||
history: 10000
|
||||
multiplier: 3
|
||||
faux_multiplier: 3
|
||||
auto_scroll: true # TODO
|
||||
|
||||
# Display tabs using this many cells (changes require restart)
|
||||
tabspaces: 4
|
||||
|
||||
# When true, bold text is drawn using the bright variant of colors.
|
||||
draw_bold_text_with_bright_colors: true
|
||||
|
||||
# Font configuration (changes require restart)
|
||||
font:
|
||||
# The normal (roman) font face to use.
|
||||
normal:
|
||||
family: Input Mono
|
||||
bold:
|
||||
family: Input Mono
|
||||
italic:
|
||||
family: Input Mono
|
||||
|
||||
# Point size of the font
|
||||
size: 12.0
|
||||
|
||||
# Scale the font size based on the monitor's DPI.
|
||||
scale_with_dpi: false
|
||||
|
||||
# Use custom cursor colors. If true, display the cursor in the cursor.foreground
|
||||
# and cursor.background colors, otherwise invert the colors of the cursor.
|
||||
custom_cursor_colors: false
|
||||
|
||||
# Colors (Gruber Darker)
|
||||
colors:
|
||||
# Default colors
|
||||
primary:
|
||||
background: '0x181818'
|
||||
foreground: '0xe4e4ef'
|
||||
|
||||
# Colors the cursor will use if `custom_cursor_colors` is true
|
||||
cursor:
|
||||
text: '0x000000'
|
||||
cursor: '0xf5f5f5'
|
||||
|
||||
# Normal colors
|
||||
normal:
|
||||
black: '0x282828'
|
||||
red: '0xf43841'
|
||||
green: '0x73c936'
|
||||
yellow: '0xffdd33'
|
||||
blue: '0x96a6c8'
|
||||
magenta: '0x9e95c7'
|
||||
cyan: '0x1fad83'
|
||||
white: '0xf5f5f5'
|
||||
|
||||
# Bright colors
|
||||
bright:
|
||||
black: '0x484848'
|
||||
red: '0xff4f58'
|
||||
green: '0x73c936'
|
||||
yellow: '0xffdd33'
|
||||
blue: '0x5f627f'
|
||||
magenta: '0x9e95c7'
|
||||
cyan: '0x1fad83'
|
||||
white: '0xffffff'
|
||||
|
||||
# Background opacity
|
||||
# Key bindings
|
||||
#
|
||||
# Each binding is defined as an object with some properties. Most of the
|
||||
# properties are optional. All of the alphabetical keys should have a letter for
|
||||
# the `key` value such as `V`. Function keys are probably what you would expect
|
||||
# as well (F1, F2, ..). The number keys above the main keyboard are encoded as
|
||||
# `Key1`, `Key2`, etc. Keys on the number pad are encoded `Number1`, `Number2`,
|
||||
# etc. These all match the glutin::VirtualKeyCode variants.
|
||||
#
|
||||
# Possible values for `mods`
|
||||
# `Command`, `Super` refer to the super/command/windows key
|
||||
# `Control` for the control key
|
||||
# `Shift` for the Shift key
|
||||
# `Alt` and `Option` refer to alt/option
|
||||
#
|
||||
# mods may be combined with a `|`. For example, requiring control and shift
|
||||
# looks like:
|
||||
#
|
||||
# mods: Control|Shift
|
||||
#
|
||||
# The parser is currently quite sensitive to whitespace and capitalization -
|
||||
# capitalization must match exactly, and piped items must not have whitespace
|
||||
# around them.
|
||||
#
|
||||
# Either an `action`, `chars`, or `command` field must be present.
|
||||
# `action` must be one of `Paste`, `PasteSelection`, `Copy`, or `Quit`.
|
||||
# `chars` writes the specified string every time that binding is activated.
|
||||
# These should generally be escape sequences, but they can be configured to
|
||||
# send arbitrary strings of bytes.
|
||||
# `command` must be a map containing a `program` string, and `args` array of
|
||||
# strings. For example:
|
||||
# - { ... , command: { program: "alacritty", args: ["-e", "vttest"] } }
|
||||
#
|
||||
# Want to add a binding (e.g. "PageUp") but are unsure what the X sequence
|
||||
# (e.g. "\x1b[5~") is? Open another terminal (like xterm) without tmux,
|
||||
# then run `showkey -a` to get the sequence associated to a key combination.
|
||||
key_bindings:
|
||||
- { key: V, mods: Control|Shift, action: Paste }
|
||||
- { key: C, mods: Control|Shift, action: Copy }
|
||||
- { key: Q, mods: Command, action: Quit }
|
||||
- { key: W, mods: Command, action: Quit }
|
||||
- { key: Insert, mods: Shift, action: PasteSelection }
|
||||
- { key: Home, chars: "\x1bOH", mode: AppCursor }
|
||||
- { key: Home, chars: "\x1b[H", mode: ~AppCursor }
|
||||
- { key: End, chars: "\x1bOF", mode: AppCursor }
|
||||
- { key: End, chars: "\x1b[F", mode: ~AppCursor }
|
||||
- { key: PageUp, mods: Shift, chars: "\x1b[5;2~" }
|
||||
- { key: PageUp, mods: Control, chars: "\x1b[5;5~" }
|
||||
- { key: PageUp, chars: "\x1b[5~" }
|
||||
- { key: PageDown, mods: Shift, chars: "\x1b[6;2~" }
|
||||
- { key: PageDown, mods: Control, chars: "\x1b[6;5~" }
|
||||
- { key: PageDown, chars: "\x1b[6~" }
|
||||
- { key: Left, mods: Shift, chars: "\x1b[1;2D" }
|
||||
- { key: Left, mods: Control, chars: "\x1b[1;5D" }
|
||||
- { key: Left, mods: Alt, chars: "\x1b[1;3D" }
|
||||
- { key: Left, chars: "\x1b[D", mode: ~AppCursor }
|
||||
- { key: Left, chars: "\x1bOD", mode: AppCursor }
|
||||
- { key: Right, mods: Shift, chars: "\x1b[1;2C" }
|
||||
- { key: Right, mods: Control, chars: "\x1b[1;5C" }
|
||||
- { key: Right, mods: Alt, chars: "\x1b[1;3C" }
|
||||
- { key: Right, chars: "\x1b[C", mode: ~AppCursor }
|
||||
- { key: Right, chars: "\x1bOC", mode: AppCursor }
|
||||
- { key: Up, mods: Shift, chars: "\x1b[1;2A" }
|
||||
- { key: Up, mods: Control, chars: "\x1b[1;5A" }
|
||||
- { key: Up, mods: Alt, chars: "\x1b[1;3A" }
|
||||
- { key: Up, chars: "\x1b[A", mode: ~AppCursor }
|
||||
- { key: Up, chars: "\x1bOA", mode: AppCursor }
|
||||
- { key: Down, mods: Shift, chars: "\x1b[1;2B" }
|
||||
- { key: Down, mods: Control, chars: "\x1b[1;5B" }
|
||||
- { key: Down, mods: Alt, chars: "\x1b[1;3B" }
|
||||
- { key: Down, chars: "\x1b[B", mode: ~AppCursor }
|
||||
- { key: Down, chars: "\x1bOB", mode: AppCursor }
|
||||
- { key: Tab, mods: Shift, chars: "\x1b[Z" }
|
||||
- { key: F1, chars: "\x1bOP" }
|
||||
- { key: F2, chars: "\x1bOQ" }
|
||||
- { key: F3, chars: "\x1bOR" }
|
||||
- { key: F4, chars: "\x1bOS" }
|
||||
- { key: F5, chars: "\x1b[15~" }
|
||||
- { key: F6, chars: "\x1b[17~" }
|
||||
- { key: F7, chars: "\x1b[18~" }
|
||||
- { key: F8, chars: "\x1b[19~" }
|
||||
- { key: F9, chars: "\x1b[20~" }
|
||||
- { key: F10, chars: "\x1b[21~" }
|
||||
- { key: F11, chars: "\x1b[23~" }
|
||||
- { key: F12, chars: "\x1b[24~" }
|
||||
- { key: Back, chars: "\x7f" }
|
||||
- { key: Back, mods: Alt, chars: "\x1b\x7f" }
|
||||
- { key: Insert, chars: "\x1b[2~" }
|
||||
- { key: Delete, chars: "\x1b[3~" }
|
||||
|
||||
# Mouse bindings
|
||||
#
|
||||
# Currently doesn't support modifiers. Both the `mouse` and `action` fields must
|
||||
# be specified.
|
||||
#
|
||||
# Values for `mouse`:
|
||||
# - Middle
|
||||
# - Left
|
||||
# - Right
|
||||
# - Numeric identifier such as `5`
|
||||
#
|
||||
# Values for `action`:
|
||||
# - Paste
|
||||
# - PasteSelection
|
||||
# - Copy (TODO)
|
||||
mouse_bindings:
|
||||
- { mouse: Middle, action: PasteSelection }
|
||||
|
||||
mouse:
|
||||
double_click: { threshold: 300 }
|
||||
triple_click: { threshold: 300 }
|
||||
|
||||
selection:
|
||||
semantic_escape_chars: ",│`|:\"' ()[]{}<>"
|
||||
background_opacity: 1.0
|
||||
|
||||
hide_cursor_when_typing: false
|
||||
|
||||
# Live config reload (changes require restart)
|
||||
live_config_reload: true
|
||||
|
||||
# Disable visual bell
|
||||
visual_bell:
|
||||
duration: 0
|
||||
40
ops/infra/nixos/dotfiles/config.fish
Normal file
40
ops/infra/nixos/dotfiles/config.fish
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Configure classic prompt
|
||||
set fish_color_user --bold blue
|
||||
set fish_color_cwd --bold white
|
||||
|
||||
# Enable colour hints in VCS prompt:
|
||||
set __fish_git_prompt_showcolorhints yes
|
||||
set __fish_git_prompt_color_prefix purple
|
||||
set __fish_git_prompt_color_suffix purple
|
||||
|
||||
# Fish configuration
|
||||
set fish_greeting ""
|
||||
set PATH $HOME/.local/bin $HOME/.cargo/bin $PATH
|
||||
|
||||
# Editor configuration
|
||||
set -gx EDITOR "emacsclient"
|
||||
set -gx ALTERNATE_EDITOR "emacs -q -nw"
|
||||
set -gx VISUAL "emacsclient"
|
||||
|
||||
# Miscellaneous
|
||||
eval (direnv hook fish)
|
||||
|
||||
# Useful command aliases
|
||||
alias gpr 'git pull --rebase'
|
||||
alias gco 'git checkout'
|
||||
alias gf 'git fetch'
|
||||
alias gap 'git add -p'
|
||||
alias pbcopy 'xclip -selection clipboard'
|
||||
alias edit 'emacsclient -n'
|
||||
alias servedir 'nix-shell -p haskellPackages.wai-app-static --run warp'
|
||||
|
||||
# Old habits die hard (also ls is just easier to type):
|
||||
alias ls 'exa'
|
||||
|
||||
# Fix up nix-env & friends for Nix 2.0
|
||||
export NIX_REMOTE=daemon
|
||||
|
||||
# Fix display of fish in emacs' term-mode:
|
||||
function fish_title
|
||||
true
|
||||
end
|
||||
16
ops/infra/nixos/dotfiles/msmtprc
Normal file
16
ops/infra/nixos/dotfiles/msmtprc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
defaults
|
||||
|
||||
port 587
|
||||
tls on
|
||||
tls_trust_file /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
# Runbox mail
|
||||
account runbox
|
||||
from mail@tazj.in
|
||||
host mail.runbox.com
|
||||
auth on
|
||||
user mail@tazj.in
|
||||
passwordeval pass show general/runbox-tazjin
|
||||
|
||||
# Use Runbox as default
|
||||
account default : runbox
|
||||
21
ops/infra/nixos/dotfiles/notmuch-config
Normal file
21
ops/infra/nixos/dotfiles/notmuch-config
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# .notmuch-config - Configuration file for the notmuch mail system
|
||||
#
|
||||
# For more information about notmuch, see https://notmuchmail.org
|
||||
|
||||
[database]
|
||||
path=/home/vincent/mail
|
||||
|
||||
[user]
|
||||
name=Vincent Ambo
|
||||
primary_email=mail@tazj.in
|
||||
other_email=tazjin@gmail.com;
|
||||
|
||||
[new]
|
||||
tags=unread;inbox;
|
||||
ignore=
|
||||
|
||||
[search]
|
||||
exclude_tags=deleted;spam;draft;
|
||||
|
||||
[maildir]
|
||||
synchronize_flags=true
|
||||
39
ops/infra/nixos/dotfiles/offlineimaprc
Normal file
39
ops/infra/nixos/dotfiles/offlineimaprc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
[general]
|
||||
accounts = tazjin, gmail
|
||||
|
||||
[DEFAULT]
|
||||
ssl = yes
|
||||
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
# Private GMail account (old):
|
||||
[Account gmail]
|
||||
maxage = 90
|
||||
localrepository = gmail-local
|
||||
remoterepository = gmail-remote
|
||||
synclabels = yes
|
||||
|
||||
[Repository gmail-local]
|
||||
type = GmailMaildir
|
||||
localfolders = ~/mail/gmail
|
||||
|
||||
[Repository gmail-remote]
|
||||
type = Gmail
|
||||
remoteuser = tazjin@gmail.com
|
||||
remotepassfile = ~/.config/mail/gmail-pass
|
||||
folderfilter = lambda folder: folder == 'INBOX'
|
||||
|
||||
# Main private account:
|
||||
[Account tazjin]
|
||||
localrepository = tazjin-local
|
||||
remoterepository = tazjin-remote
|
||||
|
||||
[Repository tazjin-local]
|
||||
type = Maildir
|
||||
localfolders = ~/mail/tazjin
|
||||
|
||||
[Repository tazjin-remote]
|
||||
type = IMAP
|
||||
remotehost = mail.runbox.com
|
||||
remoteuser = mail@tazj.in
|
||||
remotepassfile = ~/.config/mail/tazjin-pass
|
||||
auth_mechanisms = LOGIN
|
||||
14
ops/infra/nixos/dotfiles/tmux.conf
Normal file
14
ops/infra/nixos/dotfiles/tmux.conf
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
set -g status off
|
||||
set -gw mode-keys emacs
|
||||
setw -g mouse on
|
||||
|
||||
# Correctly set window titles
|
||||
set -g set-titles on
|
||||
set -g set-titles-string "#W (#T)"
|
||||
|
||||
# List of plugins
|
||||
set -g @plugin 'tmux-plugins/tpm'
|
||||
set -g @plugin 'tmux-plugins/tmux-yank'
|
||||
|
||||
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
|
||||
run '~/.tmux/plugins/tpm/tpm'
|
||||
75
ops/infra/nixos/home.nix
Normal file
75
ops/infra/nixos/home.nix
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# home-manager configuration used on ChromeOS systems
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
# Allow non-free software (fonts, IDEA, etc.):
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# Install various useful packages:
|
||||
home.packages = with pkgs; [
|
||||
bat
|
||||
exa
|
||||
gnupg
|
||||
google-cloud-sdk
|
||||
htop
|
||||
pass
|
||||
ripgrep
|
||||
tdesktop
|
||||
transmission
|
||||
tree
|
||||
|
||||
# Fonts to make available in X11 applications:
|
||||
input-fonts
|
||||
|
||||
# Emacs configuration stays in the normal ~/.emacs.d location (for
|
||||
# now), hence this package is not installed via `programs.emacs`.
|
||||
(import ./emacs.nix { inherit pkgs; })
|
||||
];
|
||||
|
||||
programs.git = {
|
||||
enable = true;
|
||||
userEmail = "mail@tazj.in";
|
||||
userName = "Vincent Ambo";
|
||||
};
|
||||
|
||||
services.gpg-agent = {
|
||||
enable = true;
|
||||
extraConfig = ''
|
||||
pinentry-program ${pkgs.pinentry}/bin/pinentry-gtk-2
|
||||
allow-emacs-pinentry
|
||||
'';
|
||||
};
|
||||
|
||||
# Let Home Manager install and manage itself.
|
||||
programs.home-manager.enable = true;
|
||||
manual.html.enable = true;
|
||||
|
||||
# Shell configuration
|
||||
#
|
||||
# There are some differences between the ChromeOS / NixOS
|
||||
# configurations, so instead of fixing up the dotfile to support
|
||||
# both I opted for keeping the configuration here.
|
||||
programs.fish = {
|
||||
enable = true;
|
||||
interactiveShellInit = ''
|
||||
# Configure classic prompt
|
||||
set fish_color_user --bold blue
|
||||
set fish_color_cwd --bold white
|
||||
|
||||
# Enable colour hints in VCS prompt:
|
||||
set __fish_git_prompt_showcolorhints yes
|
||||
set __fish_git_prompt_color_prefix purple
|
||||
set __fish_git_prompt_color_suffix purple
|
||||
|
||||
# Fish configuration
|
||||
set fish_greeting ""
|
||||
|
||||
# Fix up nix-env & friends for Nix 2.0
|
||||
export NIX_REMOTE=daemon
|
||||
'';
|
||||
};
|
||||
|
||||
# Ensure fonts installed via Nix are picked up.
|
||||
fonts.fontconfig.enableProfileFonts = true;
|
||||
}
|
||||
77
ops/infra/nixos/mail.nix
Normal file
77
ops/infra/nixos/mail.nix
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# This file configures offlineimap, notmuch and MSMTP.
|
||||
#
|
||||
# Some manual configuration is required the first time this is
|
||||
# applied:
|
||||
#
|
||||
# 1. Credential setup.
|
||||
# 2. Linking of MSMTP config (ln -s /etc/msmtprc ~/.msmtprc)
|
||||
# 3. Linking of notmuch config (ln -s /etc/notmuch-config ~/.notmuch-config)
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let offlineImapConfig = pkgs.writeText "offlineimaprc"
|
||||
(builtins.readFile ./dotfiles/offlineimaprc);
|
||||
|
||||
msmtpConfig = pkgs.writeText "msmtprc"
|
||||
(builtins.readFile ./dotfiles/msmtprc);
|
||||
|
||||
notmuchConfig = pkgs.writeText "notmuch-config"
|
||||
(builtins.readFile ./dotfiles/notmuch-config);
|
||||
|
||||
tagConfig = pkgs.writeText "notmuch-tags" ''
|
||||
# Tag emacs-devel mailing list:
|
||||
-inbox +emacs-devel -- to:emacs-devel@gnu.org OR cc:emacs-devel@gnu.org
|
||||
|
||||
# Tag nix-devel mailing list & discourse:
|
||||
-inbox +nix-devel -- to:nix-devel@googlegroups.com OR from:nixos1@discoursemail.com
|
||||
|
||||
# Tag my own mail (from other devices) as sent:
|
||||
-inbox +sent -- from:mail@tazj.in
|
||||
|
||||
# Drafts are always read, duh.
|
||||
-unread -- tag:draft
|
||||
'';
|
||||
|
||||
notmuchIndex = pkgs.writeShellScriptBin "notmuch-index" ''
|
||||
echo "Indexing new mails in notmuch"
|
||||
|
||||
# Index new mail
|
||||
${pkgs.notmuch}/bin/notmuch new
|
||||
|
||||
# Apply tags
|
||||
cat ${tagConfig} | ${pkgs.notmuch}/bin/notmuch tag --batch
|
||||
|
||||
echo "Done indexing new mails"
|
||||
'';
|
||||
in {
|
||||
# Enable OfflineIMAP timer & service:
|
||||
systemd.user.timers.offlineimap = {
|
||||
description = "OfflineIMAP timer";
|
||||
wantedBy = [ "timers.target" ];
|
||||
|
||||
timerConfig = {
|
||||
Unit = "offlineimap.service";
|
||||
OnCalendar = "*:0/2"; # every 2 minutes
|
||||
Persistent = "true"; # persist timer state after reboots
|
||||
};
|
||||
};
|
||||
|
||||
systemd.user.services.offlineimap = {
|
||||
description = "OfflineIMAP service";
|
||||
path = with pkgs; [ pass notmuch ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${pkgs.offlineimap}/bin/offlineimap -u syslog -o -c ${offlineImapConfig}";
|
||||
ExecStartPost = "${notmuchIndex}/bin/notmuch-index";
|
||||
TimeoutStartSec = "2min";
|
||||
};
|
||||
};
|
||||
|
||||
# Link configuration files to /etc/ (from where they will be linked
|
||||
# further):
|
||||
environment.etc = {
|
||||
"msmtprc".source = msmtpConfig;
|
||||
"notmuch-config".source = notmuchConfig;
|
||||
};
|
||||
}
|
||||
132
ops/infra/nixos/packages.nix
Normal file
132
ops/infra/nixos/packages.nix
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# This file contains configuration for packages to install.
|
||||
# It does not contain configuration for software that is already covered
|
||||
# by other NixOS options (e.g. emacs)
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
let
|
||||
fetchChannel = { rev, sha256 }: import (fetchTarball {
|
||||
inherit sha256;
|
||||
url = "https://github.com/NixOS/nixpkgs-channels/archive/${rev}.tar.gz";
|
||||
}) { config.allowUnfree = true; };
|
||||
|
||||
# Channels last updated: 2018-10-10
|
||||
#
|
||||
# Instead of relying on Nix channels and ending up with out-of-sync
|
||||
# situations between machines, the commit for the stable Nix channel
|
||||
# is pinned here.
|
||||
stable = fetchChannel {
|
||||
rev = "d96c7a356383302db4426a0d5a8383af921d964f";
|
||||
sha256 = "0hlhczh3m077rwrhp4smf3zd2sfj38h2c126bycv66m0aff0gycn";
|
||||
};
|
||||
|
||||
# Certain packages from unstable are hand-picked into the package
|
||||
# set.
|
||||
unstable = fetchChannel {
|
||||
rev = "32bcd72bf28a971c9063a9cdcc32effe49f49331";
|
||||
sha256 = "1f74m18r6xl9s55jbkj9bjhdxg2489kwjam4d96pf9rzq0i1f8li";
|
||||
};
|
||||
in {
|
||||
# Configure the Nix package manager
|
||||
nixpkgs = {
|
||||
config.allowUnfree = true;
|
||||
# To use the pinned channel, the original package set is thrown
|
||||
# away in the overrides:
|
||||
config.packageOverrides = oldPkgs: stable // {
|
||||
# Store whole unstable channel in case that other modules need
|
||||
# it (see emacs.nix for example):
|
||||
inherit unstable;
|
||||
|
||||
# Backport Exa from unstable until a fix for the Rust builder is
|
||||
# backported.
|
||||
#
|
||||
# https://github.com/NixOS/nixpkgs/pull/48020
|
||||
exa = unstable.exa;
|
||||
|
||||
wallpapers = import ./pkgs/wallpapers.nix;
|
||||
pulseaudio-ctl = import pkgs/pulseaudio-ctl.nix;
|
||||
};
|
||||
};
|
||||
|
||||
# ... and declare packages to be installed.
|
||||
environment.systemPackages = with pkgs; [
|
||||
# Default nixos.* packages:
|
||||
alacritty
|
||||
binutils-unwrapped
|
||||
chromium
|
||||
curl
|
||||
direnv
|
||||
dnsutils
|
||||
dotnet-sdk
|
||||
evince
|
||||
exa
|
||||
extremetuxracer
|
||||
fd
|
||||
file
|
||||
firefox-unwrapped
|
||||
fish
|
||||
gcc
|
||||
git
|
||||
gnumake
|
||||
gnupg
|
||||
google-cloud-sdk
|
||||
gopass
|
||||
hicolor-icon-theme
|
||||
htop
|
||||
i3lock
|
||||
iftop
|
||||
jq
|
||||
kontemplate
|
||||
kubernetes
|
||||
lispPackages.quicklisp
|
||||
lxappearance-gtk3
|
||||
manpages
|
||||
maven
|
||||
mono
|
||||
mq-cli
|
||||
msmtp
|
||||
ngrok
|
||||
notmuch
|
||||
numix-cursor-theme
|
||||
numix-gtk-theme
|
||||
numix-icon-theme
|
||||
offlineimap
|
||||
openjdk
|
||||
openssl
|
||||
openssl.dev
|
||||
pass
|
||||
pavucontrol
|
||||
pkgconfig
|
||||
pulseaudio-ctl
|
||||
pwgen
|
||||
ripgrep
|
||||
rustup
|
||||
sbcl
|
||||
screen
|
||||
siege
|
||||
spotify
|
||||
stdmanpages
|
||||
systemd.dev
|
||||
tdesktop
|
||||
terraform
|
||||
tig
|
||||
tmux
|
||||
tokei
|
||||
transmission
|
||||
tree
|
||||
units
|
||||
unzip
|
||||
vlc
|
||||
xclip
|
||||
xfce.xfce4-screenshooter
|
||||
|
||||
# Haskell packages:
|
||||
cabal-install
|
||||
ghc
|
||||
hlint
|
||||
stack
|
||||
stack2nix
|
||||
haskellPackages.stylish-haskell
|
||||
haskellPackages.yesod-bin
|
||||
];
|
||||
}
|
||||
39
ops/infra/nixos/stallo-configuration.nix
Normal file
39
ops/infra/nixos/stallo-configuration.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Local configuration for 'stallo' (Home desktop PC)
|
||||
{ config, pkgs, ...}:
|
||||
|
||||
{
|
||||
boot.initrd.luks.devices.stallo-luks.device = "/dev/disk/by-uuid/b484cf1e-a27b-4785-8bd6-fa85a004b073";
|
||||
|
||||
# Use proprietary nvidia driver
|
||||
services.xserver.videoDrivers = [ "nvidia" ];
|
||||
|
||||
# Enable 32-bit compatibility for Steam:
|
||||
hardware.opengl.driSupport32Bit = true;
|
||||
hardware.pulseaudio.support32Bit = true;
|
||||
|
||||
# Wine for Blizzard stuff
|
||||
environment.systemPackages = with pkgs.unstable; [ wineWowPackages.staging winetricks ];
|
||||
|
||||
networking = {
|
||||
hostName = "stallo";
|
||||
wireless.enable = true;
|
||||
wireless.networks = {
|
||||
# Welcome to roast club!
|
||||
|
||||
"How do I computer fast?" = {
|
||||
psk = "washyourface";
|
||||
# Prefer 5Ghz unless the card is acting up.
|
||||
priority = 10;
|
||||
};
|
||||
|
||||
"How do I computer?" = {
|
||||
psk = "washyourface";
|
||||
};
|
||||
};
|
||||
# IPv6 at home, of course:
|
||||
nameservers = [
|
||||
"2606:4700:4700::1111"
|
||||
"2606:4700:4700::1001"
|
||||
];
|
||||
};
|
||||
}
|
||||
3
ops/journaldriver/.gitignore
vendored
Normal file
3
ops/journaldriver/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
result
|
||||
/target
|
||||
**/*.rs.bk
|
||||
816
ops/journaldriver/Cargo.lock
generated
Normal file
816
ops/journaldriver/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,816 @@
|
|||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ascii"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chunked_transfer"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cstr-argument"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon-sys"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "journaldriver"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"medallion 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"systemd 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ureq 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libsystemd-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "medallion"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qstring"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "systemd"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cstr-argument 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libsystemd-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-cstr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"qstring 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-cstr"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wincolor"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"
|
||||
"checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
|
||||
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
|
||||
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781"
|
||||
"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
|
||||
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
|
||||
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
|
||||
"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf"
|
||||
"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980"
|
||||
"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa"
|
||||
"checksum cstr-argument 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "514570a4b719329df37f93448a70df2baac553020d0eb43a8dfa9c1f5ba7b658"
|
||||
"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
|
||||
"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
|
||||
"checksum failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "946d0e98a50d9831f5d589038d2ca7f8f455b1c21028c0db0e84116a12696426"
|
||||
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
|
||||
"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
|
||||
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
|
||||
"checksum libsystemd-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e751b723417158e0949ba470bee4affd6f1dd6b67622b5240d79186631b6a0d9"
|
||||
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
|
||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
"checksum medallion 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2e6f0713b388174fc3de9b63a0a63dfcee191a8abc8e06c0a9c6d80821c1891"
|
||||
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
|
||||
"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b"
|
||||
"checksum native-tls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8b0a7bd714e83db15676d31caf968ad7318e9cc35f93c85a90231c8f22867549"
|
||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||
"checksum openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2e79eede055813a3ac52fb3915caf8e1c9da2dec1587871aec9f6f7b48508d"
|
||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
"checksum openssl-sys 0.9.36 (registry+https://github.com/rust-lang/crates.io-index)" = "409d77eeb492a1aebd6eb322b2ee72ff7c7496b4434d98b3bf8be038755de65e"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
|
||||
"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
|
||||
"checksum qstring 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "545ec057a36a93e25fb5883baed912e4984af4e2543bbf0e3463d962e0408469"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
|
||||
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
|
||||
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
|
||||
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
|
||||
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
|
||||
"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
|
||||
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
|
||||
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
|
||||
"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
|
||||
"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
|
||||
"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56"
|
||||
"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0"
|
||||
"checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf"
|
||||
"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9"
|
||||
"checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe"
|
||||
"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce"
|
||||
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
|
||||
"checksum syn 0.15.8 (registry+https://github.com/rust-lang/crates.io-index)" = "356d1c5043597c40489e9af2d2498c7fefc33e99b7d75b43be336c8a59b3e45e"
|
||||
"checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7"
|
||||
"checksum systemd 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b62a732355787f960c25536210ae0a981aca2e5dae9dab8491bdae39613ce48"
|
||||
"checksum tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "55c1195ef8513f3273d55ff59fe5da6940287a0d7a98331254397f464833675b"
|
||||
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
|
||||
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum ureq 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f3f941c0434783c82e46d30508834be5f3c1f2c85dd1b98f0681984c7be8e03"
|
||||
"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
|
||||
"checksum utf8-cstr 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55bcbb425141152b10d5693095950b51c3745d019363fc2929ffd8f61449b628"
|
||||
"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4"
|
||||
"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
|
||||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
|
||||
21
ops/journaldriver/Cargo.toml
Normal file
21
ops/journaldriver/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "journaldriver"
|
||||
version = "1.1.0"
|
||||
authors = ["Vincent Ambo <mail@tazj.in>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", features = [ "serde" ]}
|
||||
env_logger = "0.5"
|
||||
failure = "0.1"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
medallion = "2.2"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
systemd = "0.3"
|
||||
ureq = { version = "0.6.2", features = [ "json" ]}
|
||||
|
||||
[build-dependencies]
|
||||
pkg-config = "0.3"
|
||||
152
ops/journaldriver/README.md
Normal file
152
ops/journaldriver/README.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
journaldriver
|
||||
=============
|
||||
|
||||
This is a small daemon used to forward logs from `journald` (systemd's
|
||||
logging service) to [Stackdriver Logging][].
|
||||
|
||||
Many existing log services are written in inefficient dynamic
|
||||
languages with error-prone "cover every possible use-case"
|
||||
configuration. `journaldriver` instead aims to fit a specific use-case
|
||||
very well, instead of covering every possible logging setup.
|
||||
|
||||
`journaldriver` can be run on GCP-instances with no additional
|
||||
configuration as authentication tokens are retrieved from the
|
||||
[metadata server][].
|
||||
|
||||
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
|
||||
**Table of Contents**
|
||||
|
||||
- [Features](#features)
|
||||
- [Usage on Google Cloud Platform](#usage-on-google-cloud-platform)
|
||||
- [Usage outside of Google Cloud Platform](#usage-outside-of-google-cloud-platform)
|
||||
- [Log levels / severities / priorities](#log-levels--severities--priorities)
|
||||
- [NixOS module](#nixos-module)
|
||||
- [Stackdriver Error Reporting](#stackdriver-error-reporting)
|
||||
|
||||
<!-- markdown-toc end -->
|
||||
|
||||
# Features
|
||||
|
||||
* `journaldriver` persists the last forwarded position in the journal
|
||||
and will resume forwarding at the same position after a restart
|
||||
* `journaldriver` will recognise log entries in JSON format and
|
||||
forward them appropriately to make structured log entries available
|
||||
in Stackdriver
|
||||
* `journaldriver` can be used outside of GCP by configuring static
|
||||
credentials
|
||||
* `journaldriver` will recognise journald's log priority levels and
|
||||
convert them into equivalent Stackdriver log severity levels
|
||||
|
||||
# Usage on Google Cloud Platform
|
||||
|
||||
`journaldriver` does not require any configuration when running on GCP
|
||||
instances.
|
||||
|
||||
1. Install `journaldriver` on the instance from which you wish to
|
||||
forward logs.
|
||||
|
||||
2. Ensure that the instance has the appropriate permissions to write
|
||||
to Stackdriver. Google continously changes how IAM is implemented
|
||||
on GCP, so you will have to refer to [Google's documentation][].
|
||||
|
||||
By default instances have the required permissions if Stackdriver
|
||||
Logging support is enabled in the project.
|
||||
|
||||
3. Start `journaldriver`, for example via `systemd`.
|
||||
|
||||
# Usage outside of Google Cloud Platform
|
||||
|
||||
When running outside of GCP, the following extra steps need to be
|
||||
performed:
|
||||
|
||||
1. Create a Google Cloud Platform service account with the "Log
|
||||
Writer" role and download its private key in JSON-format.
|
||||
2. When starting `journaldriver`, configure the following environment
|
||||
variables:
|
||||
|
||||
* `GOOGLE_CLOUD_PROJECT`: Name of the GCP project to which logs
|
||||
should be written.
|
||||
* `GOOGLE_APPLICATION_CREDENTIALS`: Filesystem path to the
|
||||
JSON-file containing the service account's private key.
|
||||
* `LOG_STREAM`: Name of the target log stream in Stackdriver Logging.
|
||||
This will be automatically created if it does not yet exist.
|
||||
* `LOG_NAME`: Name of the target log to write to. This defaults to
|
||||
`journaldriver` if unset, but it is recommended to - for
|
||||
example - set it to the machine hostname.
|
||||
|
||||
# Log levels / severities / priorities
|
||||
|
||||
`journaldriver` recognises [journald's priorities][] and converts them
|
||||
into [equivalent severities][] in Stackdriver. Both sets of values
|
||||
correspond to standard `syslog` priorities.
|
||||
|
||||
The easiest way to emit log messages with priorites from an
|
||||
application is to use [priority prefixes][], which are compatible with
|
||||
structured log messages.
|
||||
|
||||
For example, to emit a simple warning message (structured and
|
||||
unstructured):
|
||||
|
||||
```
|
||||
$ echo '<4>{"fnord":true, "msg":"structured log (warning)"}' | systemd-cat
|
||||
$ echo '<4>unstructured log (warning)' | systemd-cat
|
||||
```
|
||||
|
||||
# NixOS module
|
||||
|
||||
The NixOS package repository [contains a module][] for setting up
|
||||
`journaldriver` on NixOS machines. NixOS by default uses `systemd` for
|
||||
service management and `journald` for logging, which means that log
|
||||
output from most services will be captured automatically.
|
||||
|
||||
On a GCP instance the only required option is this:
|
||||
|
||||
```nix
|
||||
services.journaldriver.enable = true;
|
||||
```
|
||||
|
||||
When running outside of GCP, the configuration looks as follows:
|
||||
|
||||
```nix
|
||||
services.journaldriver = {
|
||||
enable = true;
|
||||
logStream = "prod-environment";
|
||||
logName = "hostname";
|
||||
googleCloudProject = "gcp-project-name";
|
||||
applicationCredentials = keyFile;
|
||||
};
|
||||
```
|
||||
|
||||
**Note**: The `journaldriver`-module is included in stable releases of
|
||||
NixOS since NixOS 18.09.
|
||||
|
||||
# Stackdriver Error Reporting
|
||||
|
||||
The [Stackdriver Error Reporting][] service of Google's monitoring
|
||||
toolbox supports automatically detecting and correlating errors from
|
||||
log entries.
|
||||
|
||||
To use this functionality log messages must be logged in the expected
|
||||
[log format][].
|
||||
|
||||
*Note*: Reporting errors from non-GCP instances requires that the
|
||||
`LOG_STREAM` environment variable is set to the special value
|
||||
`global`.
|
||||
|
||||
This value changes the monitored resource descriptor from a log stream
|
||||
to the project-global stream. Due to a limitation in Stackdriver Error
|
||||
Reporting, this is the only way to correctly ingest errors from
|
||||
non-GCP machines. Please see [issue #4][] for more information about
|
||||
this.
|
||||
|
||||
[Stackdriver Logging]: https://cloud.google.com/logging/
|
||||
[metadata server]: https://cloud.google.com/compute/docs/storing-retrieving-metadata
|
||||
[Google's documentation]: https://cloud.google.com/logging/docs/access-control
|
||||
[NixOS]: https://nixos.org/
|
||||
[contains a module]: https://github.com/NixOS/nixpkgs/pull/42134
|
||||
[journald's priorities]: http://0pointer.de/public/systemd-man/sd-daemon.html
|
||||
[equivalent severities]: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
|
||||
[priority prefixes]: http://0pointer.de/public/systemd-man/sd-daemon.html
|
||||
[Stackdriver Error Reporting]: https://cloud.google.com/error-reporting/
|
||||
[log format]: https://cloud.google.com/error-reporting/docs/formatting-error-messages
|
||||
[issue #4]: https://github.com/tazjin/journaldriver/issues/4
|
||||
6
ops/journaldriver/build.rs
Normal file
6
ops/journaldriver/build.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
extern crate pkg_config;
|
||||
|
||||
fn main() {
|
||||
pkg_config::probe_library("libsystemd")
|
||||
.expect("Could not probe libsystemd");
|
||||
}
|
||||
9
ops/journaldriver/default.nix
Normal file
9
ops/journaldriver/default.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{ pkgs, ... }:
|
||||
|
||||
pkgs.third_party.naersk.buildPackage {
|
||||
src = ./.;
|
||||
|
||||
buildInputs = with pkgs.third_party; [
|
||||
pkgconfig openssl systemd.dev
|
||||
];
|
||||
}
|
||||
665
ops/journaldriver/src/main.rs
Normal file
665
ops/journaldriver/src/main.rs
Normal file
|
|
@ -0,0 +1,665 @@
|
|||
// Copyright (C) 2018 Vincent Ambo <mail@tazj.in>
|
||||
//
|
||||
// journaldriver is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! This file implements journaldriver, a small application that
|
||||
//! forwards logs from journald (systemd's log facility) to
|
||||
//! Stackdriver Logging.
|
||||
//!
|
||||
//! Log entries are read continously from journald and are forwarded
|
||||
//! to Stackdriver in batches.
|
||||
//!
|
||||
//! Stackdriver Logging has a concept of monitored resources. In the
|
||||
//! simplest case this monitored resource will be the GCE instance on
|
||||
//! which journaldriver is running.
|
||||
//!
|
||||
//! Information about the instance, the project and required security
|
||||
//! credentials are retrieved from Google's metadata instance on GCP.
|
||||
//!
|
||||
//! To run journaldriver on non-GCP machines, users must specify the
|
||||
//! `GOOGLE_APPLICATION_CREDENTIALS`, `GOOGLE_CLOUD_PROJECT` and
|
||||
//! `LOG_NAME` environment variables.
|
||||
|
||||
#[macro_use] extern crate failure;
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
#[macro_use] extern crate serde_json;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
|
||||
extern crate chrono;
|
||||
extern crate env_logger;
|
||||
extern crate medallion;
|
||||
extern crate serde;
|
||||
extern crate systemd;
|
||||
extern crate ureq;
|
||||
|
||||
use chrono::offset::LocalResult;
|
||||
use chrono::prelude::*;
|
||||
use failure::ResultExt;
|
||||
use serde_json::{from_str, Value};
|
||||
use std::env;
|
||||
use std::fs::{self, File, rename};
|
||||
use std::io::{self, Read, ErrorKind, Write};
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use std::time::{Duration, Instant};
|
||||
use systemd::journal::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
const LOGGING_SERVICE: &str = "https://logging.googleapis.com/google.logging.v2.LoggingServiceV2";
|
||||
const ENTRIES_WRITE_URL: &str = "https://logging.googleapis.com/v2/entries:write";
|
||||
const METADATA_TOKEN_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
|
||||
const METADATA_ID_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/id";
|
||||
const METADATA_ZONE_URL: &str = "http://metadata.google.internal/computeMetadata/v1/instance/zone";
|
||||
const METADATA_PROJECT_URL: &str = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
|
||||
|
||||
/// Convenience type alias for results using failure's `Error` type.
|
||||
type Result<T> = std::result::Result<T, failure::Error>;
|
||||
|
||||
/// Representation of static service account credentials for GCP.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Credentials {
|
||||
/// PEM encoded private key
|
||||
private_key: String,
|
||||
|
||||
/// `kid` of this private key
|
||||
private_key_id: String,
|
||||
|
||||
/// "email" address of the service account
|
||||
client_email: String,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// ID of the GCP project to which to send logs.
|
||||
static ref PROJECT_ID: String = get_project_id();
|
||||
|
||||
/// Name of the log to write to (this should only be manually
|
||||
/// configured if not running on GCP):
|
||||
static ref LOG_NAME: String = env::var("LOG_NAME")
|
||||
.unwrap_or("journaldriver".into());
|
||||
|
||||
/// Service account credentials (if configured)
|
||||
static ref SERVICE_ACCOUNT_CREDENTIALS: Option<Credentials> =
|
||||
env::var("GOOGLE_APPLICATION_CREDENTIALS").ok()
|
||||
.and_then(|path| File::open(path).ok())
|
||||
.and_then(|file| serde_json::from_reader(file).ok());
|
||||
|
||||
/// Descriptor of the currently monitored instance. Refer to the
|
||||
/// documentation of `determine_monitored_resource` for more
|
||||
/// information.
|
||||
static ref MONITORED_RESOURCE: Value = determine_monitored_resource();
|
||||
|
||||
/// Path to the directory in which journaldriver should persist
|
||||
/// its cursor state.
|
||||
static ref CURSOR_DIR: PathBuf = env::var("CURSOR_POSITION_DIR")
|
||||
.unwrap_or("/var/lib/journaldriver".into())
|
||||
.into();
|
||||
|
||||
/// Path to the cursor position file itself.
|
||||
static ref CURSOR_FILE: PathBuf = {
|
||||
let mut path = CURSOR_DIR.clone();
|
||||
path.push("cursor.pos");
|
||||
path
|
||||
};
|
||||
|
||||
/// Path to the temporary file used for cursor position writes.
|
||||
static ref CURSOR_TMP_FILE: PathBuf = {
|
||||
let mut path = CURSOR_DIR.clone();
|
||||
path.push("cursor.tmp");
|
||||
path
|
||||
};
|
||||
}
|
||||
|
||||
/// Convenience helper for retrieving values from the metadata server.
|
||||
fn get_metadata(url: &str) -> Result<String> {
|
||||
let response = ureq::get(url)
|
||||
.set("Metadata-Flavor", "Google")
|
||||
.timeout_connect(5000)
|
||||
.timeout_read(5000)
|
||||
.call();
|
||||
|
||||
if response.ok() {
|
||||
// Whitespace is trimmed to remove newlines from responses.
|
||||
let body = response.into_string()
|
||||
.context("Failed to decode metadata response")?
|
||||
.trim().to_string();
|
||||
|
||||
Ok(body)
|
||||
} else {
|
||||
let status = response.status_line().to_string();
|
||||
let body = response.into_string()
|
||||
.unwrap_or_else(|e| format!("Metadata body error: {}", e));
|
||||
bail!("Metadata failure: {} ({})", body, status)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience helper for determining the project ID.
|
||||
fn get_project_id() -> String {
|
||||
env::var("GOOGLE_CLOUD_PROJECT")
|
||||
.map_err(Into::into)
|
||||
.or_else(|_: failure::Error| get_metadata(METADATA_PROJECT_URL))
|
||||
.expect("Could not determine project ID")
|
||||
}
|
||||
|
||||
/// Determines the monitored resource descriptor used in Stackdriver
|
||||
/// logs. On GCP this will be set to the instance ID as returned by
|
||||
/// the metadata server.
|
||||
///
|
||||
/// On non-GCP machines the value is determined by using the
|
||||
/// `GOOGLE_CLOUD_PROJECT` and `LOG_STREAM` environment variables.
|
||||
///
|
||||
/// [issue #4]: https://github.com/tazjin/journaldriver/issues/4
|
||||
fn determine_monitored_resource() -> Value {
|
||||
if let Ok(log) = env::var("LOG_STREAM") {
|
||||
// The special value `global` is recognised as a log stream name that
|
||||
// results in a `global`-type resource descriptor. This is useful in
|
||||
// cases where Stackdriver Error Reporting is intended to be used on
|
||||
// a non-GCE instance. See [issue #4][] for details.
|
||||
if log == "global" {
|
||||
return json!({
|
||||
"type": "global",
|
||||
"labels": {
|
||||
"project_id": PROJECT_ID.as_str(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
json!({
|
||||
"type": "logging_log",
|
||||
"labels": {
|
||||
"project_id": PROJECT_ID.as_str(),
|
||||
"name": log,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let instance_id = get_metadata(METADATA_ID_URL)
|
||||
.expect("Could not determine instance ID");
|
||||
|
||||
let zone = get_metadata(METADATA_ZONE_URL)
|
||||
.expect("Could not determine instance zone");
|
||||
|
||||
json!({
|
||||
"type": "gce_instance",
|
||||
"labels": {
|
||||
"project_id": PROJECT_ID.as_str(),
|
||||
"instance_id": instance_id,
|
||||
"zone": zone,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the response returned by the metadata server's token
|
||||
/// endpoint. The token is normally valid for an hour.
|
||||
#[derive(Deserialize)]
|
||||
struct TokenResponse {
|
||||
expires_in: u64,
|
||||
access_token: String,
|
||||
}
|
||||
|
||||
/// Struct used to store a token together with a sensible
|
||||
/// representation of when it expires.
|
||||
struct Token {
|
||||
token: String,
|
||||
fetched_at: Instant,
|
||||
expires: Duration,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// Does this token need to be renewed?
|
||||
fn is_expired(&self) -> bool {
|
||||
self.fetched_at.elapsed() > self.expires
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves a token from the GCP metadata service. Retrieving these
|
||||
/// tokens requires no additional authentication.
|
||||
fn get_metadata_token() -> Result<Token> {
|
||||
let body = get_metadata(METADATA_TOKEN_URL)?;
|
||||
let token: TokenResponse = from_str(&body)?;
|
||||
|
||||
debug!("Fetched new token from metadata service");
|
||||
|
||||
Ok(Token {
|
||||
fetched_at: Instant::now(),
|
||||
expires: Duration::from_secs(token.expires_in / 2),
|
||||
token: token.access_token,
|
||||
})
|
||||
}
|
||||
|
||||
/// Signs a token using static client credentials configured for a
|
||||
/// service account. This service account must have been given the
|
||||
/// `Log Writer` role in Google Cloud IAM.
|
||||
///
|
||||
/// The process for creating and signing these tokens is described
|
||||
/// here:
|
||||
///
|
||||
/// https://developers.google.com/identity/protocols/OAuth2ServiceAccount#jwt-auth
|
||||
fn sign_service_account_token(credentials: &Credentials) -> Result<Token> {
|
||||
use medallion::{Algorithm, Header, Payload};
|
||||
|
||||
let iat = Utc::now();
|
||||
let exp = iat.checked_add_signed(chrono::Duration::seconds(3600))
|
||||
.ok_or_else(|| format_err!("Failed to calculate token expiry"))?;
|
||||
|
||||
let header = Header {
|
||||
alg: Algorithm::RS256,
|
||||
headers: Some(json!({
|
||||
"kid": credentials.private_key_id,
|
||||
})),
|
||||
};
|
||||
|
||||
let payload: Payload<()> = Payload {
|
||||
iss: Some(credentials.client_email.clone()),
|
||||
sub: Some(credentials.client_email.clone()),
|
||||
aud: Some(LOGGING_SERVICE.to_string()),
|
||||
iat: Some(iat.timestamp() as u64),
|
||||
exp: Some(exp.timestamp() as u64),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let token = medallion::Token::new(header, payload)
|
||||
.sign(credentials.private_key.as_bytes())
|
||||
.context("Signing service account token failed")?;
|
||||
|
||||
debug!("Signed new service account token");
|
||||
|
||||
Ok(Token {
|
||||
token,
|
||||
fetched_at: Instant::now(),
|
||||
expires: Duration::from_secs(3000),
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the authentication token either by using static client
|
||||
/// credentials, or by talking to the metadata server.
|
||||
///
|
||||
/// Which behaviour is used is controlled by the environment variable
|
||||
/// `GOOGLE_APPLICATION_CREDENTIALS`, which should be configured to
|
||||
/// point at a JSON private key file if service account authentication
|
||||
/// is to be used.
|
||||
fn get_token() -> Result<Token> {
|
||||
if let Some(credentials) = SERVICE_ACCOUNT_CREDENTIALS.as_ref() {
|
||||
sign_service_account_token(credentials)
|
||||
} else {
|
||||
get_metadata_token()
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure represents the different types of payloads
|
||||
/// supported by journaldriver.
|
||||
///
|
||||
/// Currently log entries can either contain plain text messages or
|
||||
/// structured payloads in JSON-format.
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
enum Payload {
|
||||
TextPayload {
|
||||
#[serde(rename = "textPayload")]
|
||||
text_payload: String,
|
||||
},
|
||||
JsonPayload {
|
||||
#[serde(rename = "jsonPayload")]
|
||||
json_payload: Value,
|
||||
},
|
||||
}
|
||||
|
||||
/// Attempt to parse a log message as JSON and return it as a
|
||||
/// structured payload. If parsing fails, return the entry in plain
|
||||
/// text format.
|
||||
fn message_to_payload(message: Option<String>) -> Payload {
|
||||
match message {
|
||||
None => Payload::TextPayload { text_payload: "empty log entry".into() },
|
||||
Some(text_payload) => {
|
||||
// Attempt to deserialize the text payload as a generic
|
||||
// JSON value.
|
||||
if let Ok(json_payload) = serde_json::from_str::<Value>(&text_payload) {
|
||||
// If JSON-parsing succeeded on the payload, check
|
||||
// whether we parsed an object (Stackdriver does not
|
||||
// expect other types of JSON payload) and return it
|
||||
// in that case.
|
||||
if json_payload.is_object() {
|
||||
return Payload::JsonPayload { json_payload }
|
||||
}
|
||||
}
|
||||
|
||||
Payload::TextPayload { text_payload }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to parse journald's microsecond timestamps into a UTC
|
||||
/// timestamp.
|
||||
///
|
||||
/// Parse errors are dismissed and returned as empty options: There
|
||||
/// simply aren't any useful fallback mechanisms other than defaulting
|
||||
/// to ingestion time for journaldriver's use-case.
|
||||
fn parse_microseconds(input: String) -> Option<DateTime<Utc>> {
|
||||
if input.len() != 16 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let seconds: i64 = (&input[..10]).parse().ok()?;
|
||||
let micros: u32 = (&input[10..]).parse().ok()?;
|
||||
|
||||
match Utc.timestamp_opt(seconds, micros * 1000) {
|
||||
LocalResult::Single(time) => Some(time),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a journald log message priority to a
|
||||
/// Stackdriver-compatible severity number.
|
||||
///
|
||||
/// Both Stackdriver and journald specify equivalent
|
||||
/// severities/priorities. Conveniently, the names are the same.
|
||||
/// Inconveniently, the numbers are not.
|
||||
///
|
||||
/// For more information on the journald priorities, consult these
|
||||
/// man-pages:
|
||||
///
|
||||
/// * systemd.journal-fields(7) (section 'PRIORITY')
|
||||
/// * sd-daemon(3)
|
||||
/// * systemd.exec(5) (section 'SyslogLevelPrefix')
|
||||
///
|
||||
/// Note that priorities can be logged by applications via the prefix
|
||||
/// concept described in these man pages, without interfering with
|
||||
/// structured JSON-payloads.
|
||||
///
|
||||
/// For more information on the Stackdriver severity levels, please
|
||||
/// consult Google's documentation:
|
||||
///
|
||||
/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
||||
///
|
||||
/// Any unknown priority values result in no severity being set.
|
||||
fn priority_to_severity(priority: String) -> Option<u32> {
|
||||
match priority.as_ref() {
|
||||
"0" => Some(800), // emerg
|
||||
"1" => Some(700), // alert
|
||||
"2" => Some(600), // crit
|
||||
"3" => Some(500), // err
|
||||
"4" => Some(400), // warning
|
||||
"5" => Some(300), // notice
|
||||
"6" => Some(200), // info
|
||||
"7" => Some(100), // debug
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure represents a log entry in the format expected by
|
||||
/// the Stackdriver API.
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct LogEntry {
|
||||
labels: Value,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
timestamp: Option<DateTime<Utc>>,
|
||||
|
||||
#[serde(flatten)]
|
||||
payload: Payload,
|
||||
|
||||
// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
severity: Option<u32>,
|
||||
}
|
||||
|
||||
impl From<JournalRecord> for LogEntry {
|
||||
// Converts from the fields contained in a journald record to the
|
||||
// representation required by Stackdriver Logging.
|
||||
//
|
||||
// The fields are documented in systemd.journal-fields(7).
|
||||
fn from(mut record: JournalRecord) -> LogEntry {
|
||||
// The message field is technically just a convention, but
|
||||
// journald seems to default to it when ingesting unit
|
||||
// output.
|
||||
let payload = message_to_payload(record.remove("MESSAGE"));
|
||||
|
||||
// Presumably this is always set, but who can be sure
|
||||
// about anything in this world.
|
||||
let hostname = record.remove("_HOSTNAME");
|
||||
|
||||
// The unit is seemingly missing on kernel entries, but
|
||||
// present on all others.
|
||||
let unit = record.remove("_SYSTEMD_UNIT");
|
||||
|
||||
// The source timestamp (if present) is specified in
|
||||
// microseconds since epoch.
|
||||
//
|
||||
// If it is not present or can not be parsed, journaldriver
|
||||
// will not send a timestamp for the log entry and it will
|
||||
// default to the ingestion time.
|
||||
let timestamp = record
|
||||
.remove("_SOURCE_REALTIME_TIMESTAMP")
|
||||
.and_then(parse_microseconds);
|
||||
|
||||
// Journald uses syslogd's concept of priority. No idea if this is
|
||||
// always present, but it's optional in the Stackdriver API, so we just
|
||||
// omit it if we can't find or parse it.
|
||||
let severity = record
|
||||
.remove("PRIORITY")
|
||||
.and_then(priority_to_severity);
|
||||
|
||||
LogEntry {
|
||||
payload,
|
||||
timestamp,
|
||||
labels: json!({
|
||||
"host": hostname,
|
||||
"unit": unit.unwrap_or_else(|| "syslog".into()),
|
||||
}),
|
||||
severity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to read from the journal. If no new entry is present,
|
||||
/// await the next one up to the specified timeout.
|
||||
fn receive_next_record(timeout: Duration, journal: &mut Journal)
|
||||
-> Result<Option<JournalRecord>> {
|
||||
let next_record = journal.next_record()?;
|
||||
if next_record.is_some() {
|
||||
return Ok(next_record);
|
||||
}
|
||||
|
||||
Ok(journal.await_next_record(Some(timeout))?)
|
||||
}
|
||||
|
||||
/// This function starts a double-looped, blocking receiver. It will
|
||||
/// buffer messages for half a second before flushing them to
|
||||
/// Stackdriver.
|
||||
fn receiver_loop(mut journal: Journal) -> Result<()> {
|
||||
let mut token = get_token()?;
|
||||
|
||||
let mut buf: Vec<LogEntry> = Vec::new();
|
||||
let iteration = Duration::from_millis(500);
|
||||
|
||||
loop {
|
||||
trace!("Beginning outer iteration");
|
||||
let now = Instant::now();
|
||||
|
||||
loop {
|
||||
if now.elapsed() > iteration {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Ok(Some(entry)) = receive_next_record(iteration, &mut journal) {
|
||||
trace!("Received a new entry");
|
||||
buf.push(entry.into());
|
||||
}
|
||||
}
|
||||
|
||||
if !buf.is_empty() {
|
||||
let to_flush = mem::replace(&mut buf, Vec::new());
|
||||
flush(&mut token, to_flush, journal.cursor()?)?;
|
||||
}
|
||||
|
||||
trace!("Done outer iteration");
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the current cursor into `/var/journaldriver/cursor.pos`. To
|
||||
/// avoid issues with journaldriver being terminated while the cursor
|
||||
/// is still being written, this will first write the cursor into a
|
||||
/// temporary file and then move it.
|
||||
fn persist_cursor(cursor: String) -> Result<()> {
|
||||
// This code exists to aid in tracking down if there are other
|
||||
// causes of issue #2 than what has already been taken care of.
|
||||
//
|
||||
// One theory is that journald (or the Rust library to interface
|
||||
// with it) may occasionally return empty cursor strings. If this
|
||||
// is ever the case, we would like to know about it.
|
||||
if cursor.is_empty() {
|
||||
error!("Received empty journald cursor position, refusing to persist!");
|
||||
error!("Please report this message at https://github.com/tazjin/journaldriver/issues/2");
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let mut file = File::create(&*CURSOR_TMP_FILE)
|
||||
.context("Failed to create cursor file")?;
|
||||
|
||||
write!(file, "{}", cursor).context("Failed to write cursor file")?;
|
||||
|
||||
rename(&*CURSOR_TMP_FILE, &*CURSOR_FILE)
|
||||
.context("Failed to move cursor file")
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Flushes all drained records to Stackdriver. Any Stackdriver
|
||||
/// message can at most contain 1000 log entries which means they are
|
||||
/// chunked up here.
|
||||
///
|
||||
/// In some cases large payloads seem to cause errors in Stackdriver -
|
||||
/// the chunks are therefore made smaller here.
|
||||
///
|
||||
/// If flushing is successful the last cursor position will be
|
||||
/// persisted to disk.
|
||||
fn flush(token: &mut Token,
|
||||
entries: Vec<LogEntry>,
|
||||
cursor: String) -> Result<()> {
|
||||
if token.is_expired() {
|
||||
debug!("Refreshing Google metadata access token");
|
||||
let new_token = get_token()?;
|
||||
mem::replace(token, new_token);
|
||||
}
|
||||
|
||||
for chunk in entries.chunks(750) {
|
||||
let request = prepare_request(chunk);
|
||||
if let Err(write_error) = write_entries(token, request) {
|
||||
error!("Failed to write {} entries: {}", chunk.len(), write_error)
|
||||
} else {
|
||||
debug!("Wrote {} entries to Stackdriver", chunk.len())
|
||||
}
|
||||
}
|
||||
|
||||
persist_cursor(cursor)
|
||||
}
|
||||
|
||||
/// Convert a slice of log entries into the format expected by
|
||||
/// Stackdriver. This format is documented here:
|
||||
///
|
||||
/// https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries/write
|
||||
fn prepare_request(entries: &[LogEntry]) -> Value {
|
||||
json!({
|
||||
"logName": format!("projects/{}/logs/{}", PROJECT_ID.as_str(), LOG_NAME.as_str()),
|
||||
"resource": &*MONITORED_RESOURCE,
|
||||
"entries": entries,
|
||||
"partialSuccess": true
|
||||
})
|
||||
}
|
||||
|
||||
/// Perform the log entry insertion in Stackdriver Logging.
|
||||
fn write_entries(token: &Token, request: Value) -> Result<()> {
|
||||
let response = ureq::post(ENTRIES_WRITE_URL)
|
||||
.set("Authorization", format!("Bearer {}", token.token).as_str())
|
||||
// The timeout values are set relatively high, not because of
|
||||
// an expectation of Stackdriver being slow but just to
|
||||
// eventually hit an error case in case of network troubles.
|
||||
// Presumably no request in a functioning environment will
|
||||
// ever hit these limits.
|
||||
.timeout_connect(2000)
|
||||
.timeout_read(5000)
|
||||
.send_json(request);
|
||||
|
||||
if response.ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
let status = response.status_line().to_string();
|
||||
let body = response.into_string()
|
||||
.unwrap_or_else(|_| "no response body".into());
|
||||
bail!("Write failure: {} ({})", body, status)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to read the initial cursor position from the configured
|
||||
/// file. If there is no initial cursor position set, read from the
|
||||
/// tail of the log.
|
||||
///
|
||||
/// The only "acceptable" error when reading the cursor position is
|
||||
/// the cursor position file not existing, other errors are fatal
|
||||
/// because they indicate a misconfiguration of journaldriver.
|
||||
fn initial_cursor() -> Result<JournalSeek> {
|
||||
let read_result: io::Result<String> = (|| {
|
||||
let mut contents = String::new();
|
||||
let mut file = File::open(&*CURSOR_FILE)?;
|
||||
file.read_to_string(&mut contents)?;
|
||||
Ok(contents.trim().into())
|
||||
})();
|
||||
|
||||
match read_result {
|
||||
Ok(cursor) => Ok(JournalSeek::Cursor { cursor }),
|
||||
Err(ref err) if err.kind() == ErrorKind::NotFound => {
|
||||
info!("No previous cursor position, reading from journal tail");
|
||||
Ok(JournalSeek::Tail)
|
||||
},
|
||||
Err(err) => {
|
||||
(Err(err).context("Could not read cursor position"))?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main () {
|
||||
env_logger::init();
|
||||
|
||||
// The directory in which cursor positions are persisted should
|
||||
// have been created:
|
||||
if !CURSOR_DIR.exists() {
|
||||
error!("Cursor directory at '{:?}' does not exist", *CURSOR_DIR);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let cursor_position_dir = CURSOR_FILE.parent()
|
||||
.expect("Invalid cursor position file path");
|
||||
|
||||
fs::create_dir_all(cursor_position_dir)
|
||||
.expect("Could not create directory to store cursor position in");
|
||||
|
||||
let mut journal = Journal::open(JournalFiles::All, false, true)
|
||||
.expect("Failed to open systemd journal");
|
||||
|
||||
let seek_position = initial_cursor()
|
||||
.expect("Failed to determine initial cursor position");
|
||||
|
||||
match journal.seek(seek_position) {
|
||||
Ok(cursor) => info!("Opened journal at cursor '{}'", cursor),
|
||||
Err(err) => {
|
||||
error!("Failed to set initial journal position: {}", err);
|
||||
process::exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
receiver_loop(journal).expect("log receiver encountered an unexpected error");
|
||||
}
|
||||
95
ops/journaldriver/src/tests.rs
Normal file
95
ops/journaldriver/src/tests.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
use super::*;
|
||||
use serde_json::to_string;
|
||||
|
||||
#[test]
|
||||
fn test_text_entry_serialization() {
|
||||
let entry = LogEntry {
|
||||
labels: Value::Null,
|
||||
timestamp: None,
|
||||
payload: Payload::TextPayload {
|
||||
text_payload: "test entry".into(),
|
||||
},
|
||||
severity: None,
|
||||
};
|
||||
|
||||
let expected = "{\"labels\":null,\"textPayload\":\"test entry\"}";
|
||||
let result = to_string(&entry).expect("serialization failed");
|
||||
|
||||
assert_eq!(expected, result, "Plain text payload should serialize correctly")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_entry_serialization() {
|
||||
let entry = LogEntry {
|
||||
labels: Value::Null,
|
||||
timestamp: None,
|
||||
payload: Payload::JsonPayload {
|
||||
json_payload: json!({
|
||||
"message": "JSON test"
|
||||
})
|
||||
},
|
||||
severity: None,
|
||||
};
|
||||
|
||||
let expected = "{\"labels\":null,\"jsonPayload\":{\"message\":\"JSON test\"}}";
|
||||
let result = to_string(&entry).expect("serialization failed");
|
||||
|
||||
assert_eq!(expected, result, "JSOn payload should serialize correctly")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plain_text_payload() {
|
||||
let message = "plain text payload".into();
|
||||
let payload = message_to_payload(Some(message));
|
||||
let expected = Payload::TextPayload {
|
||||
text_payload: "plain text payload".into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, payload, "Plain text payload should be detected correctly");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_payload() {
|
||||
let payload = message_to_payload(None);
|
||||
let expected = Payload::TextPayload {
|
||||
text_payload: "empty log entry".into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, payload, "Empty payload should be handled correctly");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_payload() {
|
||||
let message = "{\"someKey\":\"someValue\", \"otherKey\": 42}".into();
|
||||
let payload = message_to_payload(Some(message));
|
||||
let expected = Payload::JsonPayload {
|
||||
json_payload: json!({
|
||||
"someKey": "someValue",
|
||||
"otherKey": 42
|
||||
})
|
||||
};
|
||||
|
||||
assert_eq!(expected, payload, "JSON payload should be detected correctly");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_no_object() {
|
||||
// This message can be parsed as valid JSON, but it is not an
|
||||
// object - it should be returned as a plain-text payload.
|
||||
let message = "42".into();
|
||||
let payload = message_to_payload(Some(message));
|
||||
let expected = Payload::TextPayload {
|
||||
text_payload: "42".into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, payload, "Non-object JSON payload should be plain text");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_microseconds() {
|
||||
let input: String = "1529175149291187".into();
|
||||
let expected: DateTime<Utc> = "2018-06-16T18:52:29.291187Z"
|
||||
.to_string().parse().unwrap();
|
||||
|
||||
assert_eq!(Some(expected), parse_microseconds(input));
|
||||
}
|
||||
60
ops/kms_pass.nix
Normal file
60
ops/kms_pass.nix
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# This tool mimics a subset of the interface of 'pass', but uses
|
||||
# Google Cloud KMS for encryption.
|
||||
#
|
||||
# It is intended to be compatible with how 'kontemplate' invokes
|
||||
# 'pass.'
|
||||
#
|
||||
# Only the 'show' and 'insert' commands are supported.
|
||||
|
||||
{ pkgs, kms, ... }:
|
||||
|
||||
let inherit (pkgs.third_party) google-cloud-sdk tree writeShellScriptBin;
|
||||
in (writeShellScriptBin "pass" ''
|
||||
set -eo pipefail
|
||||
|
||||
CMD="$1"
|
||||
readonly SECRET=$2
|
||||
readonly SECRET_PATH="$SECRETS_DIR/$SECRET"
|
||||
|
||||
function secret_check {
|
||||
if [[ -z $SECRET ]]; then
|
||||
echo 'Secret must be specified'
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -z $CMD ]]; then
|
||||
CMD="ls"
|
||||
fi
|
||||
|
||||
case "$CMD" in
|
||||
ls)
|
||||
${tree}/bin/tree $SECRETS_DIR
|
||||
;;
|
||||
show)
|
||||
secret_check
|
||||
${google-cloud-sdk}/bin/gcloud kms decrypt \
|
||||
--project ${kms.project} \
|
||||
--location ${kms.region} \
|
||||
--keyring ${kms.keyring} \
|
||||
--key ${kms.key} \
|
||||
--ciphertext-file $SECRET_PATH \
|
||||
--plaintext-file -
|
||||
;;
|
||||
insert)
|
||||
secret_check
|
||||
${google-cloud-sdk}/bin/gcloud kms encrypt \
|
||||
--project ${kms.project} \
|
||||
--location ${kms.region} \
|
||||
--keyring ${kms.keyring} \
|
||||
--key ${kms.key} \
|
||||
--ciphertext-file $SECRET_PATH \
|
||||
--plaintext-file -
|
||||
echo "Inserted secret '$SECRET'"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: pass show/insert <secret>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
'') // { meta.enableCI = true; }
|
||||
BIN
ops/secrets/gcsr-tazjin-password
Normal file
BIN
ops/secrets/gcsr-tazjin-password
Normal file
Binary file not shown.
BIN
ops/secrets/nixery-gcs-json
Normal file
BIN
ops/secrets/nixery-gcs-json
Normal file
Binary file not shown.
BIN
ops/secrets/nixery-gcs-pem
Normal file
BIN
ops/secrets/nixery-gcs-pem
Normal file
Binary file not shown.
BIN
ops/secrets/nixery-ssh-private
Normal file
BIN
ops/secrets/nixery-ssh-private
Normal file
Binary file not shown.
10
ops/sync-gcsr/default.nix
Normal file
10
ops/sync-gcsr/default.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{ pkgs, ... }:
|
||||
|
||||
pkgs.buildGo.program {
|
||||
name = "sync-gcsr";
|
||||
srcs = [ ./main.go ];
|
||||
|
||||
deps = with pkgs.third_party; map (p: p.gopkg) [
|
||||
gopkgs."gopkg.in".src-d.go-git
|
||||
];
|
||||
}
|
||||
92
ops/sync-gcsr/main.go
Normal file
92
ops/sync-gcsr/main.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2019 Google LLC.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// sync-gcsr implements a small utility that periodically mirrors a
|
||||
// remote Google Cloud Source Repository to a local file path.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
git "gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||
)
|
||||
|
||||
func EnvOr(key, def string) string {
|
||||
v := os.Getenv(key)
|
||||
if v == "" {
|
||||
return def
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func updateRepo(repo *git.Repository, tree *git.Worktree, opts *git.PullOptions) error {
|
||||
err := tree.Pull(opts)
|
||||
if err == git.NoErrAlreadyUpToDate {
|
||||
// nothing to do ...
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("Updated local repository mirror")
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var dest = EnvOr("SYNC_DEST", "/git/depot")
|
||||
var project = EnvOr("SYNC_PROJECT", "tazjins-infrastructure")
|
||||
var repo = EnvOr("SYNC_REPO", "depot")
|
||||
var user = os.Getenv("SYNC_USER")
|
||||
var pass = os.Getenv("SYNC_PASS")
|
||||
|
||||
log.Printf("Syncing repository '%s/%s' to destination '%s'", project, repo, dest)
|
||||
|
||||
var cloneOpts = git.CloneOptions{
|
||||
URL: fmt.Sprintf("https://source.developers.google.com/p/%s/r/%s", project, repo),
|
||||
}
|
||||
|
||||
if user != "" && pass != "" {
|
||||
cloneOpts.Auth = &http.BasicAuth{
|
||||
Username: user,
|
||||
Password: pass,
|
||||
}
|
||||
log.Println("Enabling basic authentication as user", user)
|
||||
}
|
||||
|
||||
action := "clone"
|
||||
handle, err := git.PlainClone(dest, false, &cloneOpts)
|
||||
|
||||
if err == git.ErrRepositoryAlreadyExists {
|
||||
log.Println("Repository has already been cloned!")
|
||||
handle, err = git.PlainOpen(dest)
|
||||
action = "open"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to %s repository: %s", action, err)
|
||||
} else {
|
||||
log.Println("Initiating update loop")
|
||||
}
|
||||
|
||||
tree, err := handle.Worktree()
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to open repository worktree:", err)
|
||||
}
|
||||
|
||||
pullOpts := git.PullOptions{
|
||||
Auth: cloneOpts.Auth,
|
||||
Force: true,
|
||||
}
|
||||
|
||||
for {
|
||||
if err = updateRepo(handle, tree, &pullOpts); err != nil {
|
||||
log.Fatalf("Failed to pull updated repository: %s", err)
|
||||
}
|
||||
time.Sleep(30 * time.Second) // TODO(tazjin): Config option for pull interval?
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue