Skip to content

Commit

Permalink
WIP: Add scaffolding to stand up Trillian on k8s.
Browse files Browse the repository at this point in the history
Signed-off-by: Ville Aikas <[email protected]>
  • Loading branch information
vaikas committed Feb 7, 2023
1 parent 3016080 commit c8225b6
Show file tree
Hide file tree
Showing 17 changed files with 704 additions and 14 deletions.
76 changes: 76 additions & 0 deletions .github/workflows/kind-scaffolding-e2e-createtree.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Kind scaffolding test

on:
pull_request:
branches: [ 'main', 'release-*' ]

permissions: read-all

jobs:
e2e-tests:
name: test tree creation with scaffolding
runs-on: ubuntu-latest

strategy:
fail-fast: false # Keep running if one leg fails.
matrix:
k8s-version:
- v1.22.x
- v1.23.x

env:
REGISTRY_NAME: registry.local
REGISTRY_PORT: 5000
KO_DOCKER_REPO: registry.local:5000/trillian

steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0
- uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.2.0
with:
go-version: '1.17'
check-latest: true

- uses: imjasonh/setup-ko@2c3450ca27f6e6f2b02e72a40f2163c281a1f675 # v0.4

- name: Install yq
uses: mikefarah/yq@70403375d7b96075bd68b40c434807cff1593568 # v4.25.1

- name: Setup mirror
uses: chainguard-dev/actions/setup-mirror@main
with:
mirror: mirror.gcr.io

- name: Setup kind cluster
uses: chainguard-dev/actions/setup-kind@main
with:
k8s-version: ${{ matrix.k8s-version }}
cluster-suffix: c${{ github.run_id }}.local

- name: Setup knative
uses: chainguard-dev/actions/setup-knative@main
with:
k8s-version: ${{ matrix.k8s-version }}
cluster-suffix: c${{ github.run_id }}.local

- name: Install Trillian
run: |
echo '::group:: install Trillian scaffolding'
ko apply -BRf ./examples/deployment/kubernetes/scaffolding
echo '::endgroup::'
echo '::group:::' waiting for services to come up
kubectl wait -n trillian-system --for=condition=Ready --timeout=5m ksvc --all
echo '::endgroup::'
- name: Create a tree on it
run: |
echo '::group:: install create tree job'
kubectl apply -Rf ./examples/deployment/kubernetes/createtree
echo '::endgroup::'
echo '::group:::' waiting for job to complete'
kubectl wait -n createtree --for=condition=Complete --timeout=5m jobs createtree
echo '::endgroup::'
kubectl -n createtree get cm trillian-tree -ojsonpath='{.data.treeID}'
- name: Collect diagnostics
if: ${{ failure() }}
uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main
145 changes: 145 additions & 0 deletions cmd/createtree-k8s/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2022 The Sigstore Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"flag"
"fmt"
"time"

"github.com/google/trillian"
"github.com/google/trillian/client"
"github.com/google/trillian/client/rpcflags"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/durationpb"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"knative.dev/pkg/logging"
"knative.dev/pkg/signals"
"sigs.k8s.io/release-utils/version"
)

const (
// Key in the configmap holding the value of the tree.
treeKey = "treeID"
)

var (
ns = flag.String("namespace", "", "Namespace where to update the configmap in")
cmname = flag.String("configmap", "", "Name of the configmap where the treeID lives")
adminServerAddr = flag.String("admin_server", "log-server.trillian-system.svc:80", "Address of the gRPC Trillian Admin Server (host:port)")
treeState = flag.String("tree_state", trillian.TreeState_ACTIVE.String(), "State of the new tree")
treeType = flag.String("tree_type", trillian.TreeType_LOG.String(), "Type of the new tree")
displayName = flag.String("display_name", "", "Display name of the new tree")
description = flag.String("description", "", "Description of the new tree")
maxRootDuration = flag.Duration("max_root_duration", time.Hour, "Interval after which a new signed root is produced despite no submissions; zero means never")
force = flag.Bool("force", false, "Force create a new tree and update configmap")
)

func main() {
flag.Parse()
ctx := signals.NewContext()
if *ns == "" {
logging.FromContext(ctx).Fatal("Need to specify --namespace for where to update the configmap in")
}
if *cmname == "" {
logging.FromContext(ctx).Fatal("Need to specify --configmap for which configmap to update")
}

versionInfo := version.GetVersionInfo()
logging.FromContext(ctx).Infof("running Version: %s GitCommit: %s BuildDate: %s", versionInfo.GitVersion, versionInfo.GitCommit, versionInfo.BuildDate)

config, err := rest.InClusterConfig()
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to get InClusterConfig: %v", err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to get clientset: %v", err)
}
cm, err := clientset.CoreV1().ConfigMaps(*ns).Get(ctx, *cmname, metav1.GetOptions{})
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to get the configmap %s/%s: %v", *ns, *cmname, err)
}

if cm.Data == nil {
cm.Data = make(map[string]string)
}
if treeID, ok := cm.Data[treeKey]; ok && !*force {
logging.FromContext(ctx).Infof("Found existing TreeID: %s", treeID)
return
}

tree, err := createTree(ctx)
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to create the trillian tree: %v", err)
}
cm.Data[treeKey] = fmt.Sprint(tree.TreeId)
logging.FromContext(ctx).Infof("Created a new tree %d updating configmap %s/%s", tree.TreeId, *ns, *cmname)

_, err = clientset.CoreV1().ConfigMaps(*ns).Update(ctx, cm, metav1.UpdateOptions{})
if err != nil {
logging.FromContext(ctx).Fatalf("Failed to update the configmap: %v", err)
}
}

func createTree(ctx context.Context) (*trillian.Tree, error) {
req, err := newRequest(ctx)
if err != nil {
return nil, err
}

dialOpts, err := rpcflags.NewClientDialOptionsFromFlags()
if err != nil {
return nil, errors.Wrap(err, "failed to determine dial options")
}

conn, err := grpc.Dial(*adminServerAddr, dialOpts...)
if err != nil {
return nil, errors.Wrap(err, "failed to dial")
}
defer conn.Close()

adminClient := trillian.NewTrillianAdminClient(conn)
logClient := trillian.NewTrillianLogClient(conn)

return client.CreateAndInitTree(ctx, req, adminClient, logClient)
}

func newRequest(ctx context.Context) (*trillian.CreateTreeRequest, error) {
ts, ok := trillian.TreeState_value[*treeState]
if !ok {
return nil, fmt.Errorf("unknown TreeState: %v", *treeState)
}

tt, ok := trillian.TreeType_value[*treeType]
if !ok {
return nil, fmt.Errorf("unknown TreeType: %v", *treeType)
}

ctr := &trillian.CreateTreeRequest{Tree: &trillian.Tree{
TreeState: trillian.TreeState(ts),
TreeType: trillian.TreeType(tt),
DisplayName: *displayName,
Description: *description,
MaxRootDuration: durationpb.New(*maxRootDuration),
}}
logging.FromContext(ctx).Infof("Creating Tree: %+v", ctr.Tree)

return ctr, nil
}
65 changes: 65 additions & 0 deletions examples/deployment/kubernetes/README-SCAFFOLDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Deploying onto Kubernetes

This document guides you through the process of spinning up an example Trillian
deployment on Kubernetes cluster with [Knative](https://knative.dev/docs/)
installed. It's suitable for GitHub action based e2e tests
as well as local testing using something like kind cluster.

## Prerequisites

1. You should have this repo checked out :)
1. A Kubernetes cluster with [Knative](https://knative.dev/docs/) installed. One
example for installing local kind cluster with Knative is
[here](https://github.com/sigstore/scaffolding/blob/main/getting-started.md#running-locally-on-kind)
and ignoring the sigstore parts after installing the cluster.
1. [ko](https://github.com/google/ko) installed.
1. You have `kubectl` installed.

## Process

1. Create the scaffolding parts of the Trillian system.
```shell
kubectl apply -Rf ./examples/deployment/kubernetes/scaffolding
```
This spins up a namespace `trillian-system` where it deploys
the following components:
* log-signer
* log-server
* mysql server
2. Let's make sure everything comes up ready:
```shell
kubectl wait -n trillian-system --for=condition=Ready --timeout=5m ksvc --all
```

You should see something like this:
```shell
vaikas@villes-mbp scaffolding % kubectl wait -n trillian-system --for=condition=Ready --timeout=5m ksvc --all
service.serving.knative.dev/log-server condition met
service.serving.knative.dev/log-signer condition met
```
3. Then create a tree in the Trillian:
```shell
ko apply -BRf ./examples/deployment/kubernetes/createtree
```

And make sure it completes.

```shell
kubectl wait -n createtree --for=condition=Complete --timeout=5m jobs createtree
```

4. Check out the tree:
```shell
kubectl -n createtree get cm trillian-tree -ojsonpath='{.data.treeID}'
```

You should see something like this:
```shell
vaikas@villes-mbp scaffolding % kubectl -n createtree get cm trillian-tree -ojsonpath='{.data.treeID}'
5213139395739357930%
```

5. You can then use the TreeID for example to run CTLog on
top of newly created Trillian.

6. TODO: Add examples for talking to Trillian for other things.
4 changes: 4 additions & 0 deletions examples/deployment/kubernetes/createtree/100-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
kind: Namespace
apiVersion: v1
metadata:
name: createtree
24 changes: 24 additions & 0 deletions examples/deployment/kubernetes/createtree/101-binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: createtree
name: cm-operator
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["configmaps"]
resourceNames: ["trillian-tree"]
verbs: ["get", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-cm-updater
namespace: createtree
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cm-operator
subjects:
- kind: ServiceAccount
name: createtree
namespace: createtree
12 changes: 12 additions & 0 deletions examples/deployment/kubernetes/createtree/101-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: trillian-tree
namespace: createtree
data:
__placeholder: |
###################################################################
# Just a placeholder so that reapplying this won't overwrite treeID
# if it already exists. This caused grief, do not remove.
###################################################################
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: createtree
namespace: createtree
18 changes: 18 additions & 0 deletions examples/deployment/kubernetes/createtree/300-createtree.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: batch/v1
kind: Job
metadata:
name: createtree
namespace: createtree
spec:
template:
spec:
serviceAccountName: createtree
restartPolicy: Never
containers:
- name: createtree
image: ko://github.com/google/trillian/cmd/createtree-k8s
args: [
"--namespace=createtree",
"--configmap=trillian-tree",
"--display_name=ctlogtree"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
kind: Namespace
apiVersion: v1
metadata:
name: trillian-system
Loading

0 comments on commit c8225b6

Please sign in to comment.