Skip to content

Commit

Permalink
feat: show deployment artifacts by default (#64)
Browse files Browse the repository at this point in the history
Not documenting the feature yet because I'm unsure how to handle the
server.properties configuration. On the other hand, there's immediate
value for some users if they can access the k8s config on their own
  • Loading branch information
lucapette authored Nov 20, 2023
1 parent bb7c2c5 commit 76291d7
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 38 deletions.
2 changes: 1 addition & 1 deletion cli/cmd/k8s/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var createCmd = &cobra.Command{
Short: "Creates a k8s TypeStream server",
Run: func(cmd *cobra.Command, args []string) {
log.Info("🚀 creating TypeStream server")
runner := k8s.NewRunner()
runner := k8s.NewRunner(Namespace)

err := runner.Apply(redpanda)
if err != nil {
Expand Down
14 changes: 14 additions & 0 deletions cli/cmd/k8s/k8s.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
package k8s

import (
"fmt"

"github.com/spf13/cobra"
"github.com/typestreamio/typestream/cli/pkg/k8s"
)

var Namespace string

var k8sCmd = &cobra.Command{
Use: "k8s",
Short: "Manage a kubernetes TypeStream server",
Run: func(cmd *cobra.Command, args []string) {
runner := k8s.NewRunner(Namespace)

fmt.Println(runner.Show())
},
}

func NewCommand() *cobra.Command {
return k8sCmd
}

func init() {
k8sCmd.PersistentFlags().StringVarP(&Namespace, "namespace", "n", "typestream", "kubernetes namespace to use")
}
2 changes: 1 addition & 1 deletion cli/cmd/k8s/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var seedCmd = &cobra.Command{
Short: "Seeds the local TypeStream server with some data",
Run: func(cmd *cobra.Command, args []string) {
log.Info("🚀 starting seeding process")
runner := k8s.NewRunner()
runner := k8s.NewRunner(Namespace)

err := runner.ApplySeeder()
if err != nil {
Expand Down
42 changes: 31 additions & 11 deletions cli/pkg/k8s/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,32 @@ import (
"os/exec"

"github.com/charmbracelet/log"
"github.com/typestreamio/typestream/cli/pkg/version"
)

//go:embed typestream.yaml
//go:embed typestream.yaml.tmpl
var typestreamTmpl string

//go:embed redpanda.yaml
var redpandaTmpl string

//go:embed seeder.yaml
//go:embed seeder.yaml.tmpl
var seederTmpl string

func parseTemplate(text string, imgName string) string {
type templateData struct {
Image string
Namespace string
}

func parseTemplate(text string, data templateData) string {
buf := bytes.Buffer{}
tmpl, err := template.New(fmt.Sprintf("%s-template", imgName)).Parse(text)
tmpl, err := template.New(fmt.Sprintf("%s-template", data.Image)).Parse(text)
if err != nil {
log.Fatal("💥 failed to parse typestream resources template: ", err)
}

err = tmpl.Execute(&buf, struct{ Image string }{Image: version.DockerImage(imgName)})
log.Printf("📝 data: %+v", data)

err = tmpl.Execute(&buf, data)
if err != nil {
log.Fatal("💥 failed to execute typestream resources template: ", err)
}
Expand All @@ -38,10 +44,11 @@ func parseTemplate(text string, imgName string) string {
}

type Runner struct {
namespace string
}

func NewRunner() *Runner {
return &Runner{}
func NewRunner(namespace string) *Runner {
return &Runner{namespace: namespace}
}

func (r *Runner) apply(tmpl string) error {
Expand All @@ -53,13 +60,26 @@ func (r *Runner) apply(tmpl string) error {
}

func (r *Runner) Apply(redpanda bool) error {
serverTmpl := r.Show()

if redpanda {
return r.apply(parseTemplate(typestreamTmpl, "typestream/server") + "\n\n---\n\n" + redpandaTmpl)
return r.apply(serverTmpl + "\n\n---\n\n" + redpandaTmpl)
}

return r.apply(parseTemplate(typestreamTmpl, "typestream/server"))
return r.apply(serverTmpl)
}

func (r *Runner) ApplySeeder() error {
return r.apply(parseTemplate(seederTmpl, "typestream/tools-seeder"))
seederTmpl := parseTemplate(seederTmpl, templateData{
Image: "typestream/tools-seeder",
Namespace: r.namespace,
})
return r.apply(seederTmpl)
}

func (r *Runner) Show() string {
return parseTemplate(typestreamTmpl, templateData{
Image: "typestream/server",
Namespace: r.namespace,
})
}
2 changes: 1 addition & 1 deletion cli/pkg/k8s/seeder.yaml → cli/pkg/k8s/seeder.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: batch/v1
kind: Job
metadata:
name: seeder
namespace: typestream
namespace: {{.Namespace}}
spec:
template:
spec:
Expand Down
14 changes: 8 additions & 6 deletions cli/pkg/k8s/typestream.yaml → cli/pkg/k8s/typestream.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{{if ne .Namespace "default" }}
apiVersion: v1
kind: Namespace
metadata:
name: typestream
name: {{.Namespace}}
spec: {}
status: {}

---
{{end -}}

apiVersion: v1
kind: ServiceAccount
Expand All @@ -18,7 +20,7 @@ metadata:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: typestream
namespace: {{.Namespace}}
name: typestream-service-role
rules:
- apiGroups: [""]
Expand All @@ -35,7 +37,7 @@ kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: typestream-service-role-binding
namespace: typestream
namespace: {{.Namespace}}
subjects:
- kind: ServiceAccount
name: typestream-service-account
Expand All @@ -50,7 +52,7 @@ apiVersion: v1
kind: ConfigMap
metadata:
name: server-config
namespace: typestream
namespace: {{.Namespace}}
data:
server.properties: |-
grpc.port=4242
Expand All @@ -65,7 +67,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: server
namespace: typestream
namespace: {{.Namespace}}
spec:
replicas: 1
selector:
Expand Down Expand Up @@ -102,7 +104,7 @@ apiVersion: v1
kind: Service
metadata:
name: server
namespace: typestream
namespace: {{.Namespace}}
spec:
ports:
- name: "4242"
Expand Down
16 changes: 11 additions & 5 deletions libs/konfig/src/main/kotlin/io/typestream/konfig/Konfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ class Konfig(source: InputStream) {
private val onCamelCase = "(?<=[a-z])(?=[A-Z])".toRegex()

init {
props.load(source)
val lines = source.bufferedReader().readLines().filter { !it.startsWith("#") }

props.load(lines.joinToString("\n").byteInputStream())
}

fun get(prefix: String, key: String): String? {
Expand All @@ -30,7 +32,7 @@ class Konfig(source: InputStream) {
}

fun <T : Any> decodeKlass(klass: KClass<T>): T {
val klassConfig = klass.java.getAnnotation(io.typestream.konfig.KonfigSource::class.java)
val klassConfig = klass.java.getAnnotation(KonfigSource::class.java)
requireNotNull(klassConfig) { "${klass.java.name} is not annotated with @KonfigSource" }
val prefix = klassConfig.prefix
require(klass.isData) { "${klass.java.name} is not a data class" }
Expand All @@ -47,9 +49,13 @@ class Konfig(source: InputStream) {
Int::class -> args[index] = get(prefix, "${param.name}")?.toInt()
Map::class -> {
val map = HashMap<String, Any>()
val keys = (props[prefix] as String).split(",").map { it.trim() }
keys.forEach { key ->
map[key] = decodeParams(param.type.arguments[1].type!!.classifier as KClass<*>, "$prefix.$key")
val mapKey = props[prefix]
if (mapKey is String) {
val keys = mapKey.split(",").map { it.trim() }
keys.forEach { key ->
map[key] =
decodeParams(param.type.arguments[1].type!!.classifier as KClass<*>, "$prefix.$key")
}
}

args[index] = map
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,27 @@ internal class KonfigSourceMapTest {
data class ServersConfig(val servers: Map<String, ServerConfig>)

@Test
fun `can load a simple config`() {
class App(konfig: io.typestream.konfig.Konfig) {
fun `loads a simple map config`() {
class App(konfig: Konfig) {
val serversConfig by konfig.inject<ServersConfig>()
}

val konfig = io.typestream.konfig.Konfig(FileInputStream("src/test/resources/map.properties"))
val konfig = Konfig(FileInputStream("src/test/resources/map.properties"))
val app = App(konfig)

assertThat(app.serversConfig.servers["local"]).extracting("host", "port").containsExactly("localhost", 4242)
assertThat(app.serversConfig.servers["remote"]).extracting("host", "port").containsExactly("example.com", 2424)
}

@Test
fun `loads empty map config`() {
class App(konfig: Konfig) {
val serversConfig by konfig.inject<ServersConfig>()
}

val konfig = Konfig("".byteInputStream())
val app = App(konfig)

assertThat(app.serversConfig.servers).isEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,53 @@ internal class KonfigSourceTest {
data class NestedConfig(val db: DbConfig, val server: ServerConfig)

@Test
fun `can load a simple config`() {
class App(konfig: io.typestream.konfig.Konfig) {
fun `loads a simple config`() {
class App(konfig: Konfig) {
val serverConfig by konfig.inject<ServerConfig>()
}

val konfig = io.typestream.konfig.Konfig(FileInputStream("src/test/resources/application.properties"))
val konfig = Konfig(FileInputStream("src/test/resources/application.properties"))
val app = App(konfig)

assertThat(app.serverConfig).extracting("host", "port").containsExactly("localhost", 4242)
}

@Test
fun `can load a simple config without prefix`() {
class App(konfig: io.typestream.konfig.Konfig) {
fun `loads a simple config without prefix`() {
class App(konfig: Konfig) {
val config by konfig.inject<Config>()
}

val konfig = io.typestream.konfig.Konfig(FileInputStream("src/test/resources/application.properties"))
val konfig = Konfig(FileInputStream("src/test/resources/application.properties"))
val app = App(konfig)

assertThat(app.config).extracting("serverHost", "serverPort").containsExactly("localhost", 4242)
}

@Test
fun `can load a nested config`() {
class App(konfig: io.typestream.konfig.Konfig) {
fun `loads a nested config`() {
class App(konfig: Konfig) {
val config by konfig.inject<NestedConfig>()
}

val konfig = io.typestream.konfig.Konfig(FileInputStream("src/test/resources/application.properties"))
val konfig = Konfig(FileInputStream("src/test/resources/application.properties"))
val app = App(konfig)

assertThat(app.config).extracting("db.endpoint", "server.host", "server.port")
assertThat(app.config)
.extracting("db.endpoint", "server.host", "server.port")
.containsExactly("http://db.local:5432", "localhost", 4242)
}

@Test
fun `ignores comments`() {
class App(konfig: Konfig) {
val config by konfig.inject<ServerConfig>()
}

val konfig = Konfig(FileInputStream("src/test/resources/comments.properties"))
val app = App(konfig)

assertThat(app.config).extracting("host", "port").containsExactly("localhost", 4242)
}

}
3 changes: 3 additions & 0 deletions libs/konfig/src/test/resources/comments.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# comment
server.host=localhost
server.port=4242

0 comments on commit 76291d7

Please sign in to comment.