GSoC Week 7 & 8: Breaking Cycles and Mid-Term Milestones


Weeks 7 and 8 were about dealing with the more difficult architectural problems that come up when you try to integrate things in the real world. During these two weeks, I had to deal with some of the hardest problems I've had to solve on the project so far: making sure Windows works, figuring out how to import cycle dependencies, and separating Lima's driver-specific code from the rest of the code.
But first, let me share the biggest milestone - I passed my GSoC mid-term evaluation! π
Windows Issue
Week 7 started with me diving deeper into Windows compatibility issues. The plugin system was working beautifully on macOS and Linux, but Windows continued to present unique challenges.
The most frustrating issue was with WSL2 boot script execution:
/mnt/c/Users/ANSUMA~1/AppData/Local/Temp/lima-wsl2-boot-1179415198.sh:
cannot execute: required file not found
Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
I spent a lot of time trying to fix this on my local Windows machine by turning off antivirus software, reinstalling WSL distributions, and changing temp directories. Nothing worked!
I was desperate (and curious), so I tried my changes on a friend's Windows PC, and they worked perfectly! This showed that the problem was with the environment and not with my code itself. A new environment can sometimes be the best way to debug.
It was easier to fix the problem with Windowsβ external driver discovery. The system was looking for .exe
files, but it only found regular executable files. This was fixed with a quick change to the Makefile and discovery logic:
if [ "$(GOOS)" = "windows" ]; then \
$(GO_BUILD) -o $(DRIVER_INSTALL_DIR)/lima-driver-$$drv.exe ./cmd/lima-driver-$$drv; \
if runtime.GOOS == "windows" {
if strings.HasPrefix(base, "lima-driver-") && strings.HasSuffix(base, ".exe") {
name = strings.TrimSuffix(strings.TrimPrefix(base, "lima-driver-"), ".exe")
}
} else {
if strings.HasPrefix(base, "lima-driver-") && !strings.HasSuffix(base, ".exe") {
name = strings.TrimPrefix(base, "lima-driver-")
}
}
Unit Testing of Registry Package
I also worked on improving the project's test coverage by adding unit tests for the Registry package during this time. This might not seem like a big deal compared to building problems, but good tests are what make software work.
func TestDiscoverDriversInDir(t *testing.T) {}
func TestRegister(t *testing.T) {}
func TestGet(t *testing.T) {}
...
The Import Cycle Nightmare
The real challenge of these weeks came when I started moving validation logic from the centralized limayaml
package to individual drivers. This seems straightforward in theory, but it led me into what Gophers call "import cycle hell".
The problem was a classic circular dependency:
limayaml β registry β driver β limayaml
Each package needed something from the others, creating an impossible dependency chain. I initially tried splitting the limayaml
package into smaller, more focused packages like limadefaults
, limavalidate
, and limaload
. This approach seemed logical, but I kept hitting new cycles at every turn.
After getting stuck in this "loophole of cycle import errors," I reached out to my mentors for guidance. Anders BjΓΆrklund provided invaluable insight, pointing me toward the Dependency Inversion Principle and suggesting I look at what specific parts of limayaml
were causing the cycles.
The solution turned out to be very simple: create a new limatype
package containing just the shared types and structs that multiple packages needed. This broke the cycle by providing a common dependency that didn't create circular references.
Before: limayaml β registry β driver β limayaml β
After:
limayaml β limatype β
driver β limatype β
limayaml β registry β
VM Type Resolution Refactoring
One of the most complex architectural changes was moving VM type resolution logic from the centralised defaults system to individual drivers. This change makes the system more modular but requires careful handling of edge cases.
The new logic follows a clear priority system:
Explicit VM type specified β Validate only against that driver
No VM type specified β Test configuration against all available drivers and use the first compatible one
// New driver-level validation approach
func (d *Driver) AcceptConfig(cfg *limatype.LimaYAML) error {}
This change required moving the VMResolver
out of the default system and handling several nil-pointer dereference issues that emerged during testing. The debugging process taught me a lot about Go's runtime behaviour.
Lessons Learned
These two weeks provided some of the most valuable learning experiences of the entire GSoC project:
Import Cycles Are A Lesson In Architecture
Working through import cycles has forced me to think even harder about package boundaries and dependencies. The solution is not always a complete restructuring of the entire package, but to identify what is the minimal shared interface.
Windows Development Is A completely different thing
Cross-platform development is so much more than different APIs. It includes different filesystems, different execution models and sometimes almost different environments entirely. Sometimes it isn't your code at all.
Mentorship Is The Shortest Path To Learning
When I was stuck on the import cycle issue, the lessons I got from the mentors were worth it. When someone with experience nudges you in the right direction of design patterns and principles saves hours and days of experimentation.
Current Status and Next Steps
As I write this blog post, the refactoring PR have just a few CI issues that need to be resolved around guest agent binary discovery:
Error discovering drivers: failed to find "lima-guestagent.Linux-x86_64" binary
Upcoming priorities:
Complete moving remaining driver-specific validation logic to the drivers and refactorisation of the code, and get the PR merged.
Explore performance optimisations for the guest agent connection.
Work on integrating the krun driver.
Continue refining Windows compatibility.
Reflection on Growth
At the start of the project, it was learning Go networking primitives and debugging gRPC connections. Now, it's all about package architecture, dependency management, and principles of system design. This evolution has been one of the best things about GSoC; it is not simply about implementing a feature, it is about becoming a better software developer. The import cycle problem taught me more about software architecture than any book.
I am really excited that the plugin system is now very close to being feature complete.
Next up: putting the finishing touches on the external driver system and exploring some of the exciting optional features that will push Lima's capabilities even further!
Subscribe to my newsletter
Read articles from Anshuman Sahoo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
