Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Revision Controller #1031

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ jobs:
needs:
- build_fn
- run_e2e_tests
if: github.ref == 'refs/heads/main'
# if: github.ref == 'refs/heads/main' #TODO: revert comment once tested on private branch
strategy:
matrix:
os: [ubuntu-20.04]
Expand Down Expand Up @@ -223,20 +223,41 @@ jobs:

- name: Prepare workspace
run: |
/tmp/ns prepare eks --env=staging --cluster=${{ secrets.EKS_STAGING_CLUSTER }} --aws_profile=ns-staging
/tmp/ns prepare eks --env=staging --cluster=${{ secrets.EKS_STAGING_CLUSTER }} --aws_profile=ns-staging --use_head_orchestrator

/tmp/ns prepare new-build-cluster --env=staging
/tmp/ns prepare new-build-cluster --env=staging --use_head_orchestrator

- name: Deploy staging
- name: Upload staging deploy-plan to registry
run: |
/tmp/ns deploy --env=staging \
DEPLOY_PLAN_URI=$(/tmp/ns deploy --env=staging \
internal/testdata/server/gogrpc \
internal/testdata/integrations/dockerfile/complex \
internal/testdata/integrations/golang \
internal/testdata/integrations/nodejs/yarn \
--use_prebuilts=true \
--golang_use_buildkit=true \
--run_codegen=false
--run_codegen=false \
--upload_plan_to=staging-deploy-plan \
--upload_to_registry | grep -F "Pushed plan to" | cut -f4 -d ' ')
echo "DEPLOY_PLAN_URI=$DEPLOY_PLAN_URI" >> $GITHUB_ENV

- name: Checkout ArgoCD manifests repo
uses: actions/checkout@v3
with:
repository: namespacelabs/argocd-staging-manifests
token: ${{ secrets.GH_MANIFESTS_REPO_PAT }}
path: argocd-staging-manifests

- name: Push new Revision to manifests repo
run: |
cd argocd-staging-manifests
echo 'image: ${{ env.DEPLOY_PLAN_URI }}' > values.yaml
git config --global user.name "github-actions"
git config --global user.email "[email protected]"
git add .
git commit -m "Foundation ${{ github.sha }}"
git push


- uses: k0kubun/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion internal/testdata/integrations/golang/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func main() {
port := config.Current.Port[0].Port

http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello, world! From Go"))
w.Write([]byte("Hello, world! From Giulio's Go!"))
})

log.Printf("Listening on port: %d", port)
Expand Down
128 changes: 128 additions & 0 deletions orchestration/controllers/crds/revision/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2022 Namespace Labs Inc; All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

package revision

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

type RevisionSpec struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// TODO: add comment
Image string `json:"image,omitempty"`
}

// DeepCopyInto, DeepCopy, and DeepCopyObject are generated typically with
// https://github.com/kubernetes/code-generator and are necessary to fulfil the API contract
// for custom resources.
func (in *RevisionSpec) DeepCopyInto(out *RevisionSpec) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Image = in.Image
}

func (in *RevisionSpec) DeepCopy() *RevisionSpec {
if in == nil {
return nil
}
out := new(RevisionSpec)
in.DeepCopyInto(out)
return out
}

func (in *RevisionSpec) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

type RevisionStatus struct {
// TODO: add status properties?
}

func (in *RevisionStatus) DeepCopyInto(out *RevisionStatus) {
*out = *in
}

func (in *RevisionStatus) DeepCopy() *RevisionStatus {
if in == nil {
return nil
}
out := new(RevisionStatus)
in.DeepCopyInto(out)
return out
}

type Revision struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec RevisionSpec `json:"spec,omitempty"`
Status RevisionStatus `json:"status,omitempty"`
}

func (in *Revision) DeepCopyInto(out *Revision) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}

func (in *Revision) DeepCopy() *Revision {
if in == nil {
return nil
}
out := new(Revision)
in.DeepCopyInto(out)
return out
}

func (in *Revision) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

type RevisionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Revision `json:"items"`
}

func (in *RevisionList) DeepCopyInto(out *RevisionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Revision, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}

func (in *RevisionList) DeepCopy() *RevisionList {
if in == nil {
return nil
}
out := new(RevisionList)
in.DeepCopyInto(out)
return out
}

func (in *RevisionList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
4 changes: 4 additions & 0 deletions orchestration/controllers/crds/revision/configure/binary.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
binary: {
name: "configure-revision-controller"
from: go_package: "."
}
82 changes: 82 additions & 0 deletions orchestration/controllers/crds/revision/configure/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2022 Namespace Labs Inc; All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

package main

import (
"context"
_ "embed"
"fmt"

rbacv1 "k8s.io/client-go/applyconfigurations/rbac/v1"
"namespacelabs.dev/foundation/framework/kubernetes/kubeblueprint"
"namespacelabs.dev/foundation/framework/kubernetes/kubedef"
"namespacelabs.dev/foundation/framework/kubernetes/kubeparser"
"namespacelabs.dev/foundation/framework/provisioning"
"namespacelabs.dev/foundation/internal/fnerrors"
"namespacelabs.dev/foundation/internal/runtime"
"namespacelabs.dev/foundation/schema"
)

//go:embed revisioncrd.yaml
var revisionCrd string

func main() {
h := provisioning.NewHandlers()
henv := h.MatchEnv(&schema.Environment{Runtime: "kubernetes"})
henv.HandleStack(configuration{})
provisioning.Handle(h)
}

type configuration struct{}

func (configuration) Apply(ctx context.Context, req provisioning.StackRequest, out *provisioning.ApplyOutput) error {
serviceAccount := makeServiceAccount(req.Focus.Server)

apply, err := kubeparser.Single([]byte(revisionCrd))
if err != nil {
return fnerrors.InternalError("failed to parse the HTTP gRPC Transcoder CRD: %w", err)
}

out.Invocations = append(out.Invocations, kubedef.Create{
Description: "Revision CustomResourceDefinition",
Resource: "customresourcedefinitions",
Body: apply.Resource,
UpdateIfExisting: true,
})

grant := kubeblueprint.GrantKubeACLs{
DescriptionBase: "Revision",
ServiceAccount: serviceAccount,
}

grant.Rules = append(grant.Rules, rbacv1.PolicyRule().
WithAPIGroups("k8s.namespacelabs.dev").
WithResources("revisions", "revisions/status").
WithVerbs("get", "list", "watch", "create", "update", "delete", "patch"))

// We leverage `record.EventRecorder` from "k8s.io/client-go/tools/record" which
// creates `Event` objects with the API group "". This rule ensures that
// the event objects created by the controller are accepted by the k8s API server.
grant.Rules = append(grant.Rules, rbacv1.PolicyRule().
WithAPIGroups("").
WithResources("events").
WithVerbs("create"))

if err := grant.Compile(req, kubeblueprint.NamespaceScope, out); err != nil {
return err
}

return nil
}

func (configuration) Delete(context.Context, provisioning.StackRequest, *provisioning.DeleteOutput) error {
// XXX unimplemented
return nil
}

// TODO: duplicate from orchestration/server/tool/main.go
func makeServiceAccount(srv runtime.Deployable) string {
return fmt.Sprintf("admin-%s", kubedef.MakeDeploymentId(srv))
}
36 changes: 36 additions & 0 deletions orchestration/controllers/crds/revision/configure/revisioncrd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# name must match the spec fields below, and be in the form: <plural>.<group>
name: revisions.k8s.namespacelabs.dev
spec:
# group name to use for REST API: /apis/<group>/<version>
group: k8s.namespacelabs.dev
# list of versions supported by this CustomResourceDefinition
versions:
- name: v1alpha1
# Each version can be enabled/disabled by Served flag.
served: true
# One and only one version must be marked as the storage version.
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
image:
type: string
# either Namespaced or Cluster
scope: Namespaced
names:
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
plural: revisions
# singular name to be used as an alias on the CLI and for display
singular: revision
# kind is normally the CamelCased singular type. Your resource manifests use this.
kind: Revision
# shortNames allow shorter string to match your resource on the CLI
shortNames:
- rev
54 changes: 54 additions & 0 deletions orchestration/controllers/crds/revision/deps.fn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// This file was automatically generated by Namespace.
// DO NOT EDIT. To update, re-run `ns generate`.

package revision

import (
"context"
"google.golang.org/grpc"
"namespacelabs.dev/foundation/orchestration/proto"
fncore "namespacelabs.dev/foundation/std/core"
"namespacelabs.dev/foundation/std/go/core"
"namespacelabs.dev/foundation/std/go/server"
fngrpc "namespacelabs.dev/foundation/std/grpc"
)

// Dependencies that are instantiated once for the lifetime of the service.
type ServiceDeps struct {
Orchestrator proto.OrchestrationServiceClient
Ready core.Check
OrchestratorConn *grpc.ClientConn
}

// Verify that WireService is present and has the appropriate type.
type checkWireService func(context.Context, server.Registrar, ServiceDeps)

var _ checkWireService = WireService

var (
Package__qjpb7o = &core.Package{
PackageName: "namespacelabs.dev/foundation/orchestration/controllers/crds/revision",
}

Provider__qjpb7o = core.Provider{
Package: Package__qjpb7o,
Instantiate: makeDeps__qjpb7o,
}
)

func makeDeps__qjpb7o(ctx context.Context, di core.Dependencies) (_ interface{}, err error) {
var deps ServiceDeps

if deps.Ready, err = fncore.ProvideReadinessCheck(ctx, nil); err != nil {
return nil, err
}

// package_name: "namespacelabs.dev/foundation/orchestration/service"
if deps.OrchestratorConn, err = fngrpc.ProvideConn(ctx, core.MustUnwrapProto("CjJuYW1lc3BhY2VsYWJzLmRldi9mb3VuZGF0aW9uL29yY2hlc3RyYXRpb24vc2VydmljZQ==", &fngrpc.Backend{}).(*fngrpc.Backend)); err != nil {
return nil, err
}

deps.Orchestrator = proto.NewOrchestrationServiceClient(deps.OrchestratorConn)

return deps, nil
}
Loading