Compare commits
No commits in common. "76c0cacc1b21d18f4c6b2c761d0a12ba5a011392" and "065e3904b8140e4509aee7b0f0030082475b1aa3" have entirely different histories.
76c0cacc1b
...
065e3904b8
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -833,28 +833,6 @@ version = "0.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dockerfile_builder"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ac372e31c7dd054d0fc69ca96ca36ee8d1cf79881683ad6f783c47aba3dc6e2"
|
|
||||||
dependencies = [
|
|
||||||
"dockerfile_builder_macros",
|
|
||||||
"eyre",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dockerfile_builder_macros"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b627d9019ce257916c7ada6f233cf22e1e5246b6d9426b20610218afb7fd3ec9"
|
|
||||||
dependencies = [
|
|
||||||
"eyre",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dyn-clone"
|
name = "dyn-clone"
|
||||||
version = "1.0.19"
|
version = "1.0.19"
|
||||||
@ -1385,7 +1363,6 @@ dependencies = [
|
|||||||
"cidr",
|
"cidr",
|
||||||
"derive-new",
|
"derive-new",
|
||||||
"directories",
|
"directories",
|
||||||
"dockerfile_builder",
|
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"harmony_macros",
|
"harmony_macros",
|
||||||
"harmony_types",
|
"harmony_types",
|
||||||
|
|||||||
@ -36,4 +36,3 @@ non-blank-string-rs = "1.0.4"
|
|||||||
k3d-rs = { path = "../k3d" }
|
k3d-rs = { path = "../k3d" }
|
||||||
directories = "6.0.0"
|
directories = "6.0.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
dockerfile_builder = "0.1.5"
|
|
||||||
|
|||||||
@ -57,10 +57,8 @@ impl Topology for HAClusterTopology {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl K8sclient for HAClusterTopology {
|
impl K8sclient for HAClusterTopology {
|
||||||
async fn k8s_client(&self) -> Result<Arc<K8sClient>, String> {
|
async fn k8s_client(&self) -> Result<Arc<K8sClient>, kube::Error> {
|
||||||
Ok(Arc::new(
|
Ok(Arc::new(K8sClient::try_default().await?))
|
||||||
K8sClient::try_default().await.map_err(|e| e.to_string())?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::{process::Command, sync::Arc};
|
use std::process::Command;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use inquire::Confirm;
|
use inquire::Confirm;
|
||||||
@ -13,10 +13,10 @@ use crate::{
|
|||||||
topology::LocalhostTopology,
|
topology::LocalhostTopology,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{HelmCommand, K8sclient, Topology, k8s::K8sClient};
|
use super::{HelmCommand, Topology, k8s::K8sClient};
|
||||||
|
|
||||||
struct K8sState {
|
struct K8sState {
|
||||||
client: Arc<K8sClient>,
|
_client: K8sClient,
|
||||||
_source: K8sSource,
|
_source: K8sSource,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
@ -29,23 +29,6 @@ pub struct K8sAnywhereTopology {
|
|||||||
k8s_state: OnceCell<Option<K8sState>>,
|
k8s_state: OnceCell<Option<K8sState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl K8sclient for K8sAnywhereTopology {
|
|
||||||
async fn k8s_client(&self) -> Result<Arc<K8sClient>, String> {
|
|
||||||
let state = match self.k8s_state.get() {
|
|
||||||
Some(state) => state,
|
|
||||||
None => return Err("K8s state not initialized yet".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let state = match state {
|
|
||||||
Some(state) => state,
|
|
||||||
None => return Err("K8s client initialized but empty".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(state.client.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl K8sAnywhereTopology {
|
impl K8sAnywhereTopology {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -141,7 +124,7 @@ impl K8sAnywhereTopology {
|
|||||||
let k3d = k3d_rs::K3d::new(k3d_score.installation_path, Some(k3d_score.cluster_name));
|
let k3d = k3d_rs::K3d::new(k3d_score.installation_path, Some(k3d_score.cluster_name));
|
||||||
let state = match k3d.get_client().await {
|
let state = match k3d.get_client().await {
|
||||||
Ok(client) => K8sState {
|
Ok(client) => K8sState {
|
||||||
client: Arc::new(K8sClient::new(client)),
|
_client: K8sClient::new(client),
|
||||||
_source: K8sSource::LocalK3d,
|
_source: K8sSource::LocalK3d,
|
||||||
message: "Successfully installed K3D cluster and acquired client".to_string(),
|
message: "Successfully installed K3D cluster and acquired client".to_string(),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -42,8 +42,8 @@ pub struct NetworkDomain {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait K8sclient: Send + Sync {
|
pub trait K8sclient: Send + Sync + std::fmt::Debug {
|
||||||
async fn k8s_client(&self) -> Result<Arc<K8sClient>, String>;
|
async fn k8s_client(&self) -> Result<Arc<K8sClient>, kube::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::info;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -36,11 +35,9 @@ impl Default for LAMPConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Topology + K8sclient> Score<T> for LAMPScore {
|
impl<T: Topology> Score<T> for LAMPScore {
|
||||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
Box::new(LAMPInterpret {
|
todo!()
|
||||||
score: self.clone(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
@ -60,23 +57,11 @@ impl<T: Topology + K8sclient> Interpret<T> for LAMPInterpret {
|
|||||||
inventory: &Inventory,
|
inventory: &Inventory,
|
||||||
topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
let image_name = match self.build_docker_image() {
|
|
||||||
Ok(name) => name,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(InterpretError::new(format!(
|
|
||||||
"Could not build LAMP docker image {e}"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
info!("LAMP docker image built {image_name}");
|
|
||||||
|
|
||||||
let deployment_score = K8sDeploymentScore {
|
let deployment_score = K8sDeploymentScore {
|
||||||
name: <LAMPScore as Score<T>>::name(&self.score),
|
name: <LAMPScore as Score<T>>::name(&self.score),
|
||||||
image: image_name,
|
image: "local_image".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("LAMP deployment_score {deployment_score:?}");
|
|
||||||
todo!();
|
|
||||||
deployment_score
|
deployment_score
|
||||||
.create_interpret()
|
.create_interpret()
|
||||||
.execute(inventory, topology)
|
.execute(inventory, topology)
|
||||||
@ -100,164 +85,3 @@ impl<T: Topology + K8sclient> Interpret<T> for LAMPInterpret {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use dockerfile_builder::instruction::{CMD, COPY, ENV, EXPOSE, FROM, RUN, WORKDIR};
|
|
||||||
use dockerfile_builder::{Dockerfile, instruction_builder::EnvBuilder};
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
impl LAMPInterpret {
|
|
||||||
pub fn build_dockerfile(
|
|
||||||
&self,
|
|
||||||
score: &LAMPScore,
|
|
||||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
|
||||||
let mut dockerfile = Dockerfile::new();
|
|
||||||
|
|
||||||
// Use the PHP version from the score to determine the base image
|
|
||||||
let php_version = score.php_version.to_string();
|
|
||||||
let php_major_minor = php_version
|
|
||||||
.split('.')
|
|
||||||
.take(2)
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.join(".");
|
|
||||||
|
|
||||||
// Base image selection - using official PHP image with Apache
|
|
||||||
dockerfile.push(FROM::from(format!("php:{}-apache", php_major_minor)));
|
|
||||||
|
|
||||||
// Set environment variables for PHP configuration
|
|
||||||
dockerfile.push(ENV::from("PHP_MEMORY_LIMIT=256M"));
|
|
||||||
dockerfile.push(ENV::from("PHP_MAX_EXECUTION_TIME=30"));
|
|
||||||
dockerfile.push(
|
|
||||||
EnvBuilder::builder()
|
|
||||||
.key("PHP_ERROR_REPORTING")
|
|
||||||
.value("\"E_ERROR | E_WARNING | E_PARSE\"")
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Install necessary PHP extensions and dependencies
|
|
||||||
dockerfile.push(RUN::from(
|
|
||||||
"apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
libfreetype6-dev \
|
|
||||||
libjpeg62-turbo-dev \
|
|
||||||
libpng-dev \
|
|
||||||
libzip-dev \
|
|
||||||
unzip \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*",
|
|
||||||
));
|
|
||||||
|
|
||||||
dockerfile.push(RUN::from(
|
|
||||||
"docker-php-ext-configure gd --with-freetype --with-jpeg && \
|
|
||||||
docker-php-ext-install -j$(nproc) \
|
|
||||||
gd \
|
|
||||||
mysqli \
|
|
||||||
pdo_mysql \
|
|
||||||
zip \
|
|
||||||
opcache",
|
|
||||||
));
|
|
||||||
|
|
||||||
// Copy PHP configuration
|
|
||||||
dockerfile.push(RUN::from("mkdir -p /usr/local/etc/php/conf.d/"));
|
|
||||||
|
|
||||||
// Create and copy a custom PHP configuration
|
|
||||||
let php_config = r#"
|
|
||||||
memory_limit = ${PHP_MEMORY_LIMIT}
|
|
||||||
max_execution_time = ${PHP_MAX_EXECUTION_TIME}
|
|
||||||
error_reporting = ${PHP_ERROR_REPORTING}
|
|
||||||
display_errors = Off
|
|
||||||
log_errors = On
|
|
||||||
error_log = /dev/stderr
|
|
||||||
date.timezone = UTC
|
|
||||||
|
|
||||||
; Opcache configuration for production
|
|
||||||
opcache.enable=1
|
|
||||||
opcache.memory_consumption=128
|
|
||||||
opcache.interned_strings_buffer=8
|
|
||||||
opcache.max_accelerated_files=4000
|
|
||||||
opcache.revalidate_freq=2
|
|
||||||
opcache.fast_shutdown=1
|
|
||||||
"#;
|
|
||||||
|
|
||||||
// Save this configuration to a temporary file within the project root
|
|
||||||
let config_path = Path::new(&score.config.project_root).join("docker-php.ini");
|
|
||||||
fs::write(&config_path, php_config)?;
|
|
||||||
|
|
||||||
// Reference the file within the Docker context (where the build runs)
|
|
||||||
dockerfile.push(COPY::from(
|
|
||||||
"docker-php.ini /usr/local/etc/php/conf.d/docker-php.ini",
|
|
||||||
));
|
|
||||||
|
|
||||||
// Security hardening
|
|
||||||
dockerfile.push(RUN::from(
|
|
||||||
"a2enmod headers && \
|
|
||||||
a2enmod rewrite && \
|
|
||||||
sed -i 's/ServerTokens OS/ServerTokens Prod/' /etc/apache2/conf-enabled/security.conf && \
|
|
||||||
sed -i 's/ServerSignature On/ServerSignature Off/' /etc/apache2/conf-enabled/security.conf"
|
|
||||||
));
|
|
||||||
|
|
||||||
// Create a dedicated user for running Apache
|
|
||||||
dockerfile.push(RUN::from(
|
|
||||||
"groupadd -g 1000 appuser && \
|
|
||||||
useradd -u 1000 -g appuser -m -s /bin/bash appuser && \
|
|
||||||
chown -R appuser:appuser /var/www/html",
|
|
||||||
));
|
|
||||||
|
|
||||||
// Set the working directory
|
|
||||||
dockerfile.push(WORKDIR::from("/var/www/html"));
|
|
||||||
|
|
||||||
// Copy application code from the project root to the container
|
|
||||||
// Note: In Dockerfile, the COPY context is relative to the build context
|
|
||||||
// We'll handle the actual context in the build_docker_image method
|
|
||||||
dockerfile.push(COPY::from(". /var/www/html"));
|
|
||||||
|
|
||||||
// Fix permissions
|
|
||||||
dockerfile.push(RUN::from("chown -R appuser:appuser /var/www/html"));
|
|
||||||
|
|
||||||
// Expose Apache port
|
|
||||||
dockerfile.push(EXPOSE::from("80/tcp"));
|
|
||||||
|
|
||||||
// Set the default command
|
|
||||||
dockerfile.push(CMD::from("apache2-foreground"));
|
|
||||||
|
|
||||||
// Save the Dockerfile to disk in the project root
|
|
||||||
let dockerfile_path = Path::new(&score.config.project_root).join("Dockerfile");
|
|
||||||
fs::write(&dockerfile_path, dockerfile.to_string())?;
|
|
||||||
|
|
||||||
Ok(dockerfile_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_docker_image(&self) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
info!("Generating Dockerfile");
|
|
||||||
let dockerfile = self.build_dockerfile(&self.score)?;
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Building Docker image with file {} from root {}",
|
|
||||||
dockerfile.to_string_lossy(),
|
|
||||||
self.score.config.project_root.to_string_lossy()
|
|
||||||
);
|
|
||||||
let image_name = format!("{}-php-apache", self.score.name);
|
|
||||||
let project_root = &self.score.config.project_root;
|
|
||||||
|
|
||||||
let output = std::process::Command::new("docker")
|
|
||||||
.args([
|
|
||||||
"build",
|
|
||||||
"--file",
|
|
||||||
dockerfile.to_str().unwrap(),
|
|
||||||
"-t",
|
|
||||||
&image_name,
|
|
||||||
project_root.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(format!(
|
|
||||||
"Failed to build Docker image: {}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(image_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user