Skip to content

Commit

Permalink
Implement manager.Runnable for fs
Browse files Browse the repository at this point in the history
  • Loading branch information
rquitales committed Sep 23, 2024
1 parent 2c19e92 commit f376743
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 22 deletions.
58 changes: 42 additions & 16 deletions operator/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"context"
"crypto/tls"
"flag"
"net"
Expand Down Expand Up @@ -142,15 +143,9 @@ func main() {
os.Exit(1)
}

// Create a new ProgramHandler to handle Program objects.
// Create a new ProgramHandler to handle Program objects. Both the ProgramReconciler and the file server need to
// access the ProgramHandler, so it is created here and passed to both.
pHandler := newProgramHandler(mgr.GetClient(), programFSAdvAddr)
// Start a simple file-server to serve CR files as compressed tarballs.
go func() {
// Wait until our manager is elected leader before starting the file server.
<-mgr.Elected()

startProgramFileServer(pHandler, programFSAddr)
}()

if err = (&autocontroller.WorkspaceReconciler{
Client: mgr.GetClient(),
Expand Down Expand Up @@ -196,22 +191,53 @@ func main() {
os.Exit(1)
}

// Start the file server for serving Program objects.
setupLog.Info("starting file server for program resource",
"address", programFSAddr,
"advertisedAddress", programFSAdvAddr,
)
if err := mgr.Add(pFileserver{pHandler, programFSAddr}); err != nil {
setupLog.Error(err, "unable to start file server for program resource")
os.Exit(1)
}

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}

// startProgramFileServer starts a simple file server to serve Program objects as compressed tarballs. This allows
// children pods with restricted permissions to access the Program objects, without needing read permissions granted.
func startProgramFileServer(programHandler *pulumicontroller.ProgramHandler, address string) {
setupLog.Info("starting file server to serve Program objects", "address", address, "advertisedAddress", programHandler.Address())
// pFileserver implements the manager.Runnable interface to start a simple file server to serve Program objects as
// compressed tarballs.
type pFileserver struct {
handler *pulumicontroller.ProgramHandler
address string
}

// Start starts the file server to serve Program objects as compressed tarballs.
func (fs pFileserver) Start(ctx context.Context) error {
mux := http.NewServeMux()
mux.Handle("/programs/", programHandler.HandleProgramServing())
err := http.ListenAndServe(address, mux)
if err != nil {
setupLog.Error(err, "Program file server error")
mux.Handle("/programs/", fs.handler.HandleProgramServing())

server := &http.Server{
Addr: fs.address,
Handler: mux,
}

errChan := make(chan error)
go func() {
err := server.ListenAndServe()
if err != nil {
errChan <- err
}
}()

select {
case err := <-errChan:
return err
case <-ctx.Done():
return server.Shutdown(ctx)
}
}

Expand Down
96 changes: 96 additions & 0 deletions operator/cmd/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
Copyright 2024.
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 (
"testing"

_ "k8s.io/client-go/plugin/pkg/client/auth"
)

func TestDetermineAdvAddr(t *testing.T) {
const fakehostname = "fakehostname"
t.Setenv("HOSTNAME", fakehostname)

tests := []struct {
addr string
want string
}{
{
addr: ":9090",
want: "localhost:9090",
},
{
addr: "localhost:1111",
want: "localhost:1111",
},
{
addr: "0.0.0.0:9090",
want: fakehostname + ":9090",
},
{
addr: "fake.default.svc.cluster.local:9090",
want: "fake.default.svc.cluster.local:9090",
},
}
for _, tc := range tests {
t.Run(tc.addr, func(t *testing.T) {
if got := determineAdvAddr(tc.addr); got != tc.want {
t.Errorf("determineAdvAddr() = %v, want %v", got, tc.want)
}
})
}
}

func TestEnvOrDefault(t *testing.T) {
// Set up some ENV vars for testing.
t.Setenv("TEST_ENV", "test")
t.Setenv("EMPTY_ENV", "")

tests := []struct {
name string
envName string
defaultValue string
want string
}{
{
name: "env set, default ignored",
envName: "TEST_ENV",
defaultValue: "default",
want: "test",
},
{
name: "env not set, default used",
envName: "EMPTY_ENV",
defaultValue: "default",
want: "default",
},
{
name: "env not set, no default",
envName: "EMPTY_ENV",
defaultValue: "",
want: "",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := envOrDefault(tc.envName, tc.defaultValue); got != tc.want {
t.Errorf("envOrDefault() = %v, want %v", got, tc.want)
}
})
}
}
12 changes: 6 additions & 6 deletions operator/config/crd/bases/pulumi.com_programs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ spec:
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
- jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
- jsonPath: .status.artifact.url
name: URL
type: string
name: v1
schema:
Expand Down Expand Up @@ -206,16 +206,16 @@ spec:
type: string
url:
description: |-
URL is the HTTP address of this artifact that is served by the controller. It
allows other pods to access the contents of the artifact without needing the correct
RBAC policies.
URL is the HTTP address of the artifact as exposed by the controller
managing the source spec. It can be used to retrieve the artifact for
consumption, e.g. by another controller applying the artifact contents.
type: string
required:
- url
type: object
lastResyncTime:
description: LastResyncTime contains a timestamp for the last time
a resync of the stack took place.
a resync of the Program took place.
format: date-time
type: string
observedGeneration:
Expand Down

0 comments on commit f376743

Please sign in to comment.