Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8277442
Initial plan
Copilot Dec 4, 2025
048d9f4
Initial plan
Copilot Dec 4, 2025
d334ece
Initial plan
Copilot Dec 4, 2025
a2d66a1
Add configurable HTTP and HTTPS ports for Caddy container via environ…
Copilot Dec 4, 2025
c9b92d8
Add environment variables support for ShinyProxy container
Copilot Dec 4, 2025
00d4947
Add comprehensive Copilot instructions for ShinyProxy Operator
Copilot Dec 4, 2025
4ca39df
Fix Kotlin version references in Copilot instructions
Copilot Dec 4, 2025
c9d50fd
Add documentation for environment variables feature
Copilot Dec 4, 2025
94316e3
Add validation and security checks for environment variables
Copilot Dec 4, 2025
831cc57
Optimize validation and improve type safety
Copilot Dec 4, 2025
8f6b273
Merge pull request #6 from jaredlander/copilot/set-up-copilot-instruc…
jaredlander Dec 4, 2025
8304eb2
Merge pull request #2 from jaredlander/copilot/make-caddy-port-config…
jaredlander Dec 4, 2025
6d0f9a9
Clarify environment variable ordering comments
Copilot Dec 4, 2025
ffad729
Pass environment variables from operator to ShinyProxy containers
Copilot Dec 4, 2025
c1f78fa
Add validation for environment variable keys and values
Copilot Dec 4, 2025
9c8df93
Initial plan
Copilot Dec 4, 2025
bc16cfc
Add GitHub Actions workflow for building and pushing Docker images
Copilot Dec 4, 2025
e610796
Update README with CI/CD documentation reference
Copilot Dec 4, 2025
a463380
Add build instructions and containerized build environment
Copilot Dec 4, 2025
aac7bb0
Remove debug ls commands from workflow for cleaner logs
Copilot Dec 4, 2025
6514085
Fix cache key path and Docker tag format for better compatibility
Copilot Dec 4, 2025
547c368
Add explicit GITHUB_TOKEN permissions for security
Copilot Dec 4, 2025
0e80039
Update build commands to use 'mvn clean package -DskipTests'
Copilot Dec 4, 2025
a079841
Changed the build process to either use devbox, or to have docker bui…
jaredlander Dec 4, 2025
675883d
Using correct name for jdk in devbox. Added debox scripts for setting…
jaredlander Dec 4, 2025
1ffb71c
Cleaned up docs to match the docker steps better.
jaredlander Dec 4, 2025
9a35eee
Merge pull request #4 from jaredlander/copilot/set-env-vars-for-shiny…
jaredlander Dec 4, 2025
ebefa80
Added instructions for build the docker image for the actual oeprator.
jaredlander Dec 4, 2025
d2a5516
Running dependency management berore copying the source so it will ha…
jaredlander Dec 4, 2025
dc555e9
Making it so that port mappings are for external ports, but internall…
jaredlander Dec 4, 2025
32d79fe
Change branch from develop to dev and make registry URL configurable …
Copilot Dec 4, 2025
ec0e795
Update documentation to consistently use 'dev' instead of 'develop'
Copilot Dec 4, 2025
5e642b5
Add dev branch specific tags and rename ACR to CR for generic registr…
Copilot Dec 4, 2025
aa857fe
Fix dev branch version tags to use extracted version from JAR manifest
Copilot Dec 4, 2025
6f73b44
Merge pull request #8 from jaredlander/copilot/add-github-actions-bui…
jaredlander Dec 4, 2025
a465ab7
Changing gh action to reference master branch as that's what it is st…
jaredlander Dec 4, 2025
12d454e
Using setup-java action's builtin caching
jaredlander Dec 4, 2025
a0b990e
Cleaned up readme
jaredlander Dec 4, 2025
d72024a
Made it possible to specify the name of the redis and caddy images.
jaredlander Dec 4, 2025
2d43faf
Enabling the user to specify the network name for containers spawned …
jaredlander Dec 5, 2025
e4ba3f0
Changed the hard coding of SPRING_CONFIG_IMPORT to SPRING_CONFIG_IMPO…
jaredlander Dec 9, 2025
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
192 changes: 192 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# ShinyProxy Operator - Copilot Instructions

## Project Overview

ShinyProxy Operator is a Kubernetes/Docker operator for managing ShinyProxy deployments. It's written in Kotlin and uses coroutines for asynchronous operations.

## Technology Stack

- **Language**: Kotlin 2.1.10 with language version 2.1
- **JDK**: Java 21 (required)
- **Build Tool**: Maven 3.x
- **Container Orchestration**: Kubernetes (via Fabric8 client) and Docker
- **Logging**: kotlin-logging with Log4j 2
- **Testing**: JUnit 5 (Jupiter) for unit and integration tests
- **Async**: Kotlin Coroutines

## Build and Test Commands

### Building the Project
```bash
devbox run mvn -U clean install -DskipTests
```

The build produces: `target/shinyproxy-operator-jar-with-dependencies.jar`

### Running Tests
```bash
devbox run mvn test
```

Tests require:
- Docker network: `sp-shared-network`
- Docker plugin: `grafana/loki-docker-driver:3.2.1`
- Environment variable: `SPO_DOCKER_GID` (group ID for docker group)

### License Header Management
```bash
devbox run mvn validate license:format
```

Automatically updates copyright headers. Year updates are handled automatically - don't change year manually.

## Code Style and Conventions

### File Structure
- Source code: `src/main/kotlin/`
- Test code: `src/test/kotlin/`
- Package: `eu.openanalytics.shinyproxyoperator`

### Formatting Guidelines
- **Indentation**: 4 spaces (not tabs)
- **Max line length**: 120 characters
- **Charset**: UTF-8
- **Line endings**: LF (Unix-style)
- **Trailing whitespace**: Remove
- **Final newline**: Always insert

### Kotlin Conventions
- Use `object` for singletons (e.g., `LabelFactory`)
- Use `const val` for compile-time constants
- Prefer `when` expressions over if-else chains
- Use `kotlinx.coroutines` for async operations
- Use `suspend` functions appropriately
- Use kotlin-logging's KotlinLogging for logging: `private val logger = KotlinLogging.logger {}`

### Copyright Headers
Every Kotlin source file must include the Apache License 2.0 header. Use the template in `LICENSE_HEADER`. The license plugin handles year updates automatically.

Example header structure:
```kotlin
/*
* ShinyProxy-Operator
*
* Copyright (C) 2021-2025 Open Analytics
*
* ===========================================================================
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Apache License as published by
* The Apache Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Apache License for more details.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/>
*/
```

## Architecture

### Core Components
- **Operator Interface**: `IOperator` - main entry point
- **Orchestrators**: Platform-specific implementations
- `KubernetesOperator`: For Kubernetes deployments
- `DockerOperator`: For Docker deployments
- **Event System**: `ShinyProxyEvent`, `ShinyProxyEventType`, `IEventController`
- **Model**: `ShinyProxy`, `ShinyProxyInstance`, `ShinyProxyStatus`

### Configuration
- Configuration via environment variables (see `Config` class)
- Key variable: `SPO_ORCHESTRATOR` - determines platform (kubernetes/docker)

### Implementations
- `src/main/kotlin/eu/openanalytics/shinyproxyoperator/impl/kubernetes/` - Kubernetes-specific code
- `src/main/kotlin/eu/openanalytics/shinyproxyoperator/impl/docker/` - Docker-specific code

## Testing

### Test Structure
- Integration tests: `MainIntegrationTest.kt` in both impl/kubernetes and impl/docker
- Helper classes in `src/test/kotlin/eu/openanalytics/shinyproxyoperator/helpers/`
- Test base classes provide setup/teardown functionality

### Testing Patterns
- Use `@Test` annotation from JUnit 5
- Use Kotlin test assertions: `assertEquals`, `assertTrue`, `assertFalse`, `assertNotNull`, `assertNull`
- Integration tests use `setup()` helper with closures
- Tests use coroutines with `withTimeout` for async operations
- Helper extensions in `helpers/extensions.kt`

### Test Requirements
- Tests run in a Minikube environment (for Kubernetes tests)
- Docker daemon required (for Docker tests)
- Specific images pre-pulled before tests

## Dependencies

### Key Libraries
- **Fabric8 Kubernetes Client** (7.1.0): Kubernetes API interactions
- **Docker Client** (7.0.8-OA-5): Docker API interactions (custom OpenAnalytics version)
- **Jackson** (2.18.3): JSON processing
- **Log4j** (2.24.3): Logging backend
- **Kotlin Coroutines** (1.10.1): Async programming
- **Kotlin** (2.1.10): Kotlin language and standard library
- **JUnit Jupiter** (5.11.4): Testing framework

### Repository Configuration
The project uses OpenAnalytics Nexus repositories for the custom Docker client version:
- Releases: https://nexus.openanalytics.eu/repository/releases
- Snapshots: https://nexus.openanalytics.eu/repository/snapshots

## Common Patterns

### Logging
```kotlin
private val logger = KotlinLogging.logger {}
logger.info { "Message with lazy evaluation" }
logger.warn { "Warning: ${variable}" }
```

### Error Handling
- Use `InternalException` for operator-specific errors
- Catch and log exceptions appropriately
- Use `exitProcess(1)` for fatal errors in main

### Labels
Use `LabelFactory` for consistent label creation:
- `labelsForShinyProxyInstance()` - for instances
- `labelsForShinyProxy()` - for resources

### Configuration Reading
```kotlin
val value = config.readConfigValue(default, "ENV_VAR_NAME") { it.lowercase() }
```

## Documentation

Main documentation is hosted at: https://shinyproxy.io/documentation/shinyproxy-operator/

Additional docs in `docs/` directory:
- Deployment configurations
- Prometheus integration

## CI/CD

GitHub Actions workflow in `.github/workflows/workflows.yaml`:
- Runs on Java 21
- Tests against multiple Kubernetes versions (1.30.11, 1.31.7, 1.32.3)
- Uses Minikube for integration tests
- Caches Maven dependencies

## Important Notes

- This is a critical infrastructure component for ShinyProxy deployments
- Changes should maintain backward compatibility
- Integration tests are comprehensive but can be time-consuming
- The operator manages both Kubernetes and Docker environments
- Resource cleanup is important in tests to prevent test pollution
100 changes: 100 additions & 0 deletions .github/workflows/build-and-push-docker.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Build and Push Docker Image

on:
push:
branches:
- master
- dev
tags:
- 'v*'
pull_request:
branches:
- master
- dev
workflow_dispatch:

jobs:
build-and-push:
runs-on: ubuntu-latest

permissions:
contents: read
packages: write

steps:
- name: Checkout shinyproxy-operator
uses: actions/checkout@v4
with:
path: shinyproxy-operator

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: 21
distribution: temurin
cache: 'maven'
cache-dependency-path: 'shinyproxy-operator/pom.xml'

- name: Build shinyproxy-operator JAR
run: |
cd shinyproxy-operator
mvn -U clean install -DskipTests

- name: Extract version from JAR
id: version
run: |
VERSION=$(unzip -p shinyproxy-operator/target/shinyproxy-operator-jar-with-dependencies.jar META-INF/MANIFEST.MF | grep Implementation-Version | cut -d' ' -f2 | tr -d '\r')
echo "full=$VERSION" >> $GITHUB_OUTPUT
MAJOR=$(echo $VERSION | cut -d. -f1)
echo "major=$MAJOR" >> $GITHUB_OUTPUT
MINOR=$(echo $VERSION | cut -d. -f2)
echo "minor=$MINOR" >> $GITHUB_OUTPUT
echo "Version: $VERSION (Major: $MAJOR, Minor: $MINOR)"

- name: Checkout shinyproxy-docker
uses: actions/checkout@v4
with:
repository: openanalytics/shinyproxy-docker
path: shinyproxy-docker

- name: Copy JAR to Docker build context
run: |
cp shinyproxy-operator/target/shinyproxy-operator-jar-with-dependencies.jar shinyproxy-docker/Operator/

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.CR_REGISTRY }}
username: ${{ secrets.CR_USERNAME }}
password: ${{ secrets.CR_PASSWORD }}

- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.CR_REGISTRY }}/shinyproxy-operator
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=dev-${{ steps.version.outputs.full }},enable=${{ github.ref == 'refs/heads/dev' }}
type=raw,value=dev-${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }},enable=${{ github.ref == 'refs/heads/dev' }}
type=raw,value=dev-${{ steps.version.outputs.major }},enable=${{ github.ref == 'refs/heads/dev' }}
type=sha,format=short

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: shinyproxy-docker/Operator
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
JAR_LOCATION=shinyproxy-operator-jar-with-dependencies.jar
cache-from: type=gha
cache-to: type=gha,mode=max
Loading