One of the key elements - that make Rig.dev what it is today - is our love for the Open-Source community.
Why? We believe in transparency, community, and the kind of innovation that only comes from a diverse, collaborative effort.
In this article, we will give you a detailed look into the open-source projects that power our own platform - Rig.dev. From our CLI to our frontend, each tool has been carefully selected to align with our mission and offer the best possible experience for both developers and DevOps teams. Enjoy!
About Rig.dev
Rig.dev offers an open-source application platform for Kubernetes. We empower developers to work in their own environments with elevated application abstractions, while still leveraging Kubernetes's reliability, portability, and scalability.
Our developer-friendly deployment engine simplifies the process of rolling out, managing, debugging and scaling applications. On top, our platform includes a Dashboard, CLI, and CI/CD pipelines that seamlessly integrate with GitHub Actions.
We need your help
We are not done building yet, but we would love your support on Github along the way. It motivates us to keep making Rig.dev better ๐
Our Github Project: https://github.com/rigdev/rig
1. Cobra: The Backbone of Our CLI
Cobra is a go-to library for building CLIs in Go (pun intended). What makes it a standout choice is its ease of use right from the get-go (yup, again). You can create a functional CLI with just a few lines of Go code and as your project scales, Cobra scales with you, offering both complexity and extensibility.
Why We Chose Cobra for Our CLI
Rich Functionality Out-of-the-Box
Cobra comes packed with a ton of built-in features that make CLI development a breeze:
Subcommands: Easily create nested commands, making your CLI more organized and intuitive.
Global, Local and Persistent Flags: Define flags that work across all commands, are specific to single commands or are inherited by subcommands.
Positional Arguments: Handle positional arguments effortlessly with validation functions and guards, making your CLI more flexible while retaining control.
Automatic Help Command: Cobra auto-generates help commands saving you the hassle of manual documentation - With the option of customizing the commands of course.
Extensibility: Play Well with Others
One of the key strengths of Cobra is also its extensibility. While Cobra takes care of the CLI's structure and the command-line parsing, you're free to integrate it with other tools to get the job done however you like:
Interactive CLI with PromptKit: We use PromptKit to add interactivity and style to our CLI. This makes the user experience more engaging.
Dependency Injection with UberFX: We leverage UberFX to inject dependencies directly into our commands, making the code cleaner and more maintainable.
Example: Creating and deploying a redis capsule
Create a build with a redis image.
Instances in the capsule listen on port 6379 and the same port is exposed using a load balancer.
We specify that when the container starts, it runs the command "redis-server /usr/local/etc/redis/redis.conf" which is how you start the redis server with a specific configuration file.
We then mount that config file into /usr/local/etc/redis/redis.conf from the local redis.conf file.
Finally, we deploy 3 instances
2. UberFX: Streamlining Dependency Injection in Go
UberFX is a robust dependency injection framework crafted by Uber for Go. In the absence of dependency injection, developers often find themselves manually constructing a myriad of objects or, worse, depending on unstable global state.
Why we use UberFX
We chose UberFX because we've seen it scale effectively, from small applications to massive codebases. Some of our team members have firsthand experience with its successful implementation at Uber, so we're confident in its ability to scale with Rig.dev as we grow.
Real-World Example: How We Use UberFX in Rig.dev
Structuring Components
In Rig.dev, all our components follow a similar initialization pattern. We specify the dependencies of each component in its constructor, and UberFX takes care of constructing everything in the right order. Here's a simplified example:
type service struct {
logger *zap.Logger
userRepo repository.User
secretRepo repository.Secret
projectService project_service.Service
}
type newParams struct {
fx.In
Logger *zap.Logger
UserRepo repository.User
SecretRepo repository.Secret
ProjectService project_service.Service
}
func NewService(p newParams) Service {
return &service{
logger: p.Logger,
userRepo: p.UserRepo,
secretRepo: p.SecretRepo,
projectService: p.ProjectService,
}
}
We then group the constructors of related components into Fx Modules:
var Module = fx.Module(
"service",
fx.Provide(
capsule.NewService,
user.NewService,
auth.NewService,
project.NewService,
group.NewService,
),
)
and from there it's a simple matter to construct the entire Rig server. We also use FX to execute non-constructor functions needed to setup the server:
// Slightly simplified Rig Server construction
rigServer := fx.New(
service.Module,
repository.Module,
gateway.Module,
telemetry.Module,
fx.Supply(config),
// Non-constructor function needed to setup the server
fx.Invoke(
func(cfg config.Config, s *pkg_service.Server) {
s.EmbeddedFileServer()
s.Init()
},
),
)
Fx also handles the various lifecycles of executing the Rig server, e.g.
// Simplified execution of Fx's lifecycles which power the Rig server
func run(ctx context.Context, rigServer *fx.App) error {
if err := rigServer.Start(ctx); err != nil {
return err
}
sig := <-rigServer.Wait()
var err error
if sig.ExitCode != 0 {
err = fmt.Errorf("aborted: signal %v", sig.Signal.String())
}
if err := rigServer.Stop(ctx); err != nil {
return err
}
return nil
}
UberFX has significantly simplified the way we build and run Rig.dev. It allows us to construct our application out of reusable components, streamlining the development process and ensuring that we can scale efficiently as our codebase grows.
3. Kubebuilder
Kubebuilder has established itself as the go-to tool when it comes to creating API extensions for Kubernetes. Sure, one can craft controllers using a raw Kubernetes API client, and technically, you could do this in any programming language.
But here's the catch: Kubernetes is written in Go, and so is a significant chunk of its ecosystem. To ensure seamless integration and reduce potential issues with tooling, it's makes perfect sense to stick with the most widely adopted tool in the ecosystem.
Insights into Our Upcoming Integration with Kubebuilder
As Rig.dev evolves, we're constantly looking for ways to enhance our platform's capabilities. One of the significant shifts we're planning is transitioning some of our APIs from being solely within Rig.dev to being more deeply integrated with Kubernetes. Our vision? To introduce a Custom Resource Definition (CRD) that will encapsulate our "capsule" concept (pun very much intended).
The Potential Benefits This Integration Will Bring to Our Users
By making this move, we're essentially making Rig.dev more compatible with the broader Kubernetes ecosystem. This has a couple of key advantages:
Ecosystem Compatibility: As we integrate our APIs more deeply into Kubernetes, Rig.dev becomes more compatible with the broader Kubernetes ecosystem. This means better interoperability with other tools and platforms that Kubernetes professionals use daily.
Familiarity: Developers who are already well-versed with Kubernetes APIs will find our APIs more intuitive and familiar. This reduces the learning curve and allows them to be productive right off the bat.
Flexibility: With our APIs being more Kubernetes-native, developers gain more flexibility in how they deploy, manage, and scale their applications on Rig.dev.
Our upcoming integration with Kubebuilder is all about reducing friction and making life easier for devs and DevOps teams that are already knee-deep in Kubernetes. We're excited about the added compatibility and the doors it will open for more integrated and streamlined operations.
4. Go-ContainerRegistry: Simplifying Remote Registry Operations
Go-ContainerRegistry is a Go library that provides a straightforward way to interact with container image registries. If you've ever needed to explore remote registries, pull images, or manipulate image metadata, this is the tool you'd likely reach for.
Why Do We Use Go-ContainerRegistry?
The primary reason we use Go-ContainerRegistry in Rig.dev is for its unified approach to working with container images. Whether you're dealing with various remote registries, a local daemon, or even local image files, Go-ContainerRegistry provides a consistent interface for all these operations. Here's why that's crucial for us:
Unified Interface: No need to juggle different tools or libraries when working with images. Go-ContainerRegistry offers a single, unified API for all your needs.
Cross-Registry Support: Whether you're pulling from Docker Hub, Google Container Registry, or any other registry, you can do it all through the same API calls.
Local and Remote: The library isn't just for remote operations; you can also use it to interact with your local Docker daemon or manipulate local image files.
5. Vue + Nuxt: Powering Our Frontend Development
Why We Chose Vue & Nuxt
When it comes to frontend development, time-to-market is often a critical factor. Vue, known for its simplicity, coupled with Nuxt, allowed us to roll out an MVP in record time. But don't let the simplicity fool you; Vue and Nuxt are among the most powerful frameworks you can use for frontend development.
Benefits of Using Vue & Nuxt
Simplicity & Quick Onboarding: If you're comfortable with HTML, CSS, and JavaScript, you'll find Vue to be incredibly approachable. The learning curve is less steep compared to other popular frameworks like React and Angular, making it easier to onboard new developers.
Performance & Lightweight Footprint: Vue is lightweight, which translates to better performance. When pitted against other popular frameworks like React and Angular, Vue consistently outperforms them (Benchmarks: https://krausest.github.io/js-framework-benchmark/current.html), especially in terms of initial load times and runtime efficiency.
Development Productivity: Nuxt takes Vue's simplicity and supercharges it, making it even easier to build complex applications. It handles a lot of the boilerplate and configuration for you, allowing devs to focus more on application logic and less on the underlying infrastructure.
Stellar Documentation: Both Vue and Nuxt come with comprehensive documentation, making it easier for new or existing developers to get up to speed. This is a significant advantage when it comes to scaling the team or introducing new features.
Why Do We Use Vue & Nuxt in Rig.dev?
The unified approach that Vue and Nuxt offer aligns well with our philosophy at Rig.dev. Just like we aim for a unified way of working with images across registries, daemons, and local files in the backend, Vue and Nuxt provide a cohesive and efficient way to build and manage our frontend.
Help us out!
We would appreciate it if you support us with a star on GitHub and help spread the word about Rig.dev to show your support! โค๏ธ
Our Github: https://github.com/rigdev/rig
Thanks from Team Rig.dev ๐