Skip to content

Commit

Permalink
feat: support mounting custom volumes to servers (jeremylvln#281)
Browse files Browse the repository at this point in the history
* feat(shulker-crds): add custom volume and volume mounts to MinecraftServer

* feat(shulker-operator): add custom volume and volume mounts to created MinecraftServer

* docs: add mounting volumes recipe
  • Loading branch information
jeremylvln committed Jan 2, 2024
1 parent b8f2f22 commit 5e7f774
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 3 deletions.
36 changes: 34 additions & 2 deletions docs/src/next/guide/recipes/overriding-pod-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ You can find all the properties overridable by looking at the
- [ProxyFleet](https://github.com/jeremylvln/Shulker/blob/main/packages/shulker-crds/src/v1alpha1/proxy_fleet.rs)
- [MinecraftServerFleet](https://github.com/jeremylvln/Shulker/blob/main/packages/shulker-crds/src/v1alpha1/minecraft_server_fleet.rs)

## Add environment variables
## Adding environment variables

Shulker already injects some environment variables that could
be useful. But adding your own is fully supported:
Expand All @@ -39,7 +39,7 @@ spec:

## Setting custom affinities

By default, Agones adds a \*_preferred_ scheduling on nodes
By default, Agones adds a _preferred_ scheduling on nodes
labelled with `agones.dev/role=gameserver`. However you
may want to customize more the scheduling behavior.

Expand Down Expand Up @@ -73,3 +73,35 @@ spec:
value: "my-server" // [!code focus]
effect: "NoSchedule" // [!code focus]
```

## Mounting volumes <Badge type="tip" text="servers" />

Additional volumes can be injected to the `MinecraftServer`'s
created `Pod`:

```yaml
apiVersion: shulkermc.io/v1alpha1
kind: MinecraftServer
metadata:
name: my-server
spec:
clusterRef:
name: my-cluster
podOverrides: // [!code focus]
volumeMounts: // [!code focus]
- name: my_extra_volume // [!code focus]
mountPath: /mnt/path // [!code focus]
volumes: // [!code focus]
- name: my_extra_volume // [!code focus]
emptyDir: {} // [!code focus]
```

:::warning

Agones, and thus Shulker, are not meant for data persistence but
rather ephemeral workload. While adding custom volumes to a `MinecraftServer`
is expected to work perfectly, adding some to a `MinecraftServerFleet`
will only work if your volume source support multiple mounts (it is
essentially the same as mounting the same volume to a `Deployment`).

:::
8 changes: 7 additions & 1 deletion packages/shulker-crds/src/v1alpha1/minecraft_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ pub struct MinecraftServerPodOverridesSpec {
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<ImageOverrideSpec>,

/// Extra environment variables to add to the crated `Pod`
/// Extra environment variables to add to the created `Pod`
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<Vec<k8s_openapi::api::core::v1::EnvVar>>,

Expand All @@ -170,6 +170,12 @@ pub struct MinecraftServerPodOverridesSpec {
/// Name of the ServiceAccount to use
#[serde(skip_serializing_if = "Option::is_none")]
pub service_account_name: Option<String>,

/// Extra volumesmounts to add to the created `Pod`
pub volume_mounts: Option<Vec<k8s_openapi::api::core::v1::VolumeMount>>,

/// Extra volumes to add to the created `Pod`
pub volumes: Option<Vec<k8s_openapi::api::core::v1::Volume>>,
}

/// The status object of `MinecraftServer`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ lazy_static! {
)])),
tolerations: None,
service_account_name: None,
volume_mounts: None,
volumes: None,
})
},
status: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,22 @@ impl<'a> GameServerBuilder {
}

pod_spec.tolerations = pod_overrides.tolerations.clone();

if let Some(volume_mounts_overrides) = &pod_overrides.volume_mounts {
pod_spec.containers[0]
.volume_mounts
.as_mut()
.unwrap()
.append(&mut volume_mounts_overrides.clone());
}

if let Some(volumes_override) = &pod_overrides.volumes {
pod_spec
.volumes
.as_mut()
.unwrap()
.append(&mut volumes_override.clone());
}
}

let mut pod_labels = minecraft_server.labels().clone();
Expand Down Expand Up @@ -561,6 +577,7 @@ impl<'a> GameServerBuilder {

#[cfg(test)]
mod tests {
use k8s_openapi::api::core::v1::{EmptyDirVolumeSource, Volume, VolumeMount};
use shulker_crds::resourceref::ResourceRefSpec;
use shulker_kube_utils::reconcilers::builder::ResourceBuilder;

Expand Down Expand Up @@ -607,6 +624,85 @@ mod tests {
insta::assert_yaml_snapshot!(game_server);
}

#[tokio::test]
async fn get_pod_template_spec_contains_volume_mounts() {
// G
let client = create_client_mock();
let resourceref_resolver = ResourceRefResolver::new(client);
let mut server = TEST_SERVER.clone();
server.spec.pod_overrides.as_mut().unwrap().volume_mounts = Some(vec![VolumeMount {
name: "my_extra_volume".to_string(),
mount_path: "/mnt/path".to_string(),
..VolumeMount::default()
}]);
let context = super::GameServerBuilderContext {
cluster: &TEST_CLUSTER,
agent_config: &AgentConfig {
maven_repository: constants::SHULKER_PLUGIN_REPOSITORY.to_string(),
version: constants::SHULKER_PLUGIN_VERSION.to_string(),
},
};

// W
let pod_template = super::GameServerBuilder::get_pod_template_spec(
&resourceref_resolver,
&context,
&server,
)
.await
.unwrap();

// T
let extra_volume_mount = pod_template.spec.as_ref().unwrap().containers[0]
.volume_mounts
.as_ref()
.unwrap()
.iter()
.find(|volume_mount| volume_mount.name == "my_extra_volume");
assert!(extra_volume_mount.is_some());
}

#[tokio::test]
async fn get_pod_template_spec_contains_volumes() {
// G
let client = create_client_mock();
let resourceref_resolver = ResourceRefResolver::new(client);
let mut server = TEST_SERVER.clone();
server.spec.pod_overrides.as_mut().unwrap().volumes = Some(vec![Volume {
name: "my_extra_volume".to_string(),
empty_dir: Some(EmptyDirVolumeSource::default()),
..Volume::default()
}]);
let context = super::GameServerBuilderContext {
cluster: &TEST_CLUSTER,
agent_config: &AgentConfig {
maven_repository: constants::SHULKER_PLUGIN_REPOSITORY.to_string(),
version: constants::SHULKER_PLUGIN_VERSION.to_string(),
},
};

// W
let pod_template = super::GameServerBuilder::get_pod_template_spec(
&resourceref_resolver,
&context,
&server,
)
.await
.unwrap();

// T
let extra_volume = pod_template
.spec
.as_ref()
.unwrap()
.volumes
.as_ref()
.unwrap()
.iter()
.find(|volume| volume.name == "my_extra_volume");
assert!(extra_volume.is_some());
}

#[tokio::test]
async fn get_init_env_contains_world() {
// G
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ lazy_static! {
)])),
tolerations: None,
service_account_name: None,
volume_mounts: None,
volumes: None,
})
},
},
Expand Down

0 comments on commit 5e7f774

Please sign in to comment.