Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
124 changes: 124 additions & 0 deletions .github/workflows/openstack_keystone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: OpenStack Keystone Test

on:
push:
branches:
- main
pull_request:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true

env:
RUST_LOG: DEBUG
RUST_BACKTRACE: full

jobs:
# Unit tests - always run, no secrets needed
unit_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run unit tests
working-directory: ./services/openstack-keystone
run: |
echo "::group::Running unit tests"
cargo test --lib --no-fail-fast
cargo test --doc --no-fail-fast
echo "::endgroup::"

# KeystoneCredentialProvider test with mock server
test_keystone_mock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Start mock Keystone server
run: |
python3 services/openstack-keystone/tests/mocks/keystone_mock_server.py 5000 &
echo "MOCK_PID=$!" >> $GITHUB_ENV
sleep 2
# Verify the mock server is running
curl -f -X POST http://127.0.0.1:5000/v3/auth/tokens \
-H "Content-Type: application/json" \
-d '{"auth":{"identity":{"methods":["password"],"password":{"user":{"name":"test","password":"test","domain":{"name":"Default"}}}}}}' \
-o /dev/null -w "HTTP %{http_code}\n" || exit 1
- name: Test KeystoneCredentialProvider with mock
working-directory: ./services/openstack-keystone
run: |
echo "::group::Testing KeystoneCredentialProvider with mock"
export REQSIGN_OPENSTACK_KEYSTONE_TEST_MOCK=on
export REQSIGN_OPENSTACK_KEYSTONE_MOCK_URL=http://127.0.0.1:5000/v3
cargo test --test credential_providers --no-fail-fast -- --no-capture
echo "::endgroup::"
- name: Stop mock server
if: always()
run: |
if [ ! -z "$MOCK_PID" ]; then
kill $MOCK_PID || true
fi

# Summary
test_summary:
needs:
[
unit_test,
test_keystone_mock,
]
if: always()
runs-on: ubuntu-latest
steps:
- name: Test Summary
run: |
echo "## OpenStack Keystone Test Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

# Unit tests status
if [[ "${{ needs.unit_test.result }}" == "success" ]]; then
echo "✅ Unit tests passed" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.unit_test.result }}" == "skipped" ]]; then
echo "⏭️ Unit tests skipped" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Unit tests failed" >> $GITHUB_STEP_SUMMARY
fi

# Mock tests status
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Integration Tests" >> $GITHUB_STEP_SUMMARY

if [[ "${{ needs.test_keystone_mock.result }}" == "success" ]]; then
echo "✅ Keystone mock tests passed" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.test_keystone_mock.result }}" == "skipped" ]]; then
echo "⏭️ Keystone mock tests skipped" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Keystone mock tests failed" >> $GITHUB_STEP_SUMMARY
fi

# Overall status
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.unit_test.result }}" == "success" ]] && \
( [[ "${{ needs.test_keystone_mock.result }}" == "success" ]] || [[ "${{ needs.test_keystone_mock.result }}" == "skipped" ]] ); then
echo "### ✅ All tests completed successfully" >> $GITHUB_STEP_SUMMARY
else
echo "### ❌ Some tests failed" >> $GITHUB_STEP_SUMMARY
exit 1
fi
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ reqsign-file-read-tokio = { version = "2.0.2", path = "context/file-read-tokio"
reqsign-google = { version = "2.0.2", path = "services/google" }
reqsign-http-send-reqwest = { version = "3.0.0", path = "context/http-send-reqwest" }
reqsign-huaweicloud-obs = { version = "2.0.2", path = "services/huaweicloud-obs" }
reqsign-openstack-keystone = { version = "2.0.0", path = "services/openstack-keystone" }
reqsign-oracle = { version = "2.0.2", path = "services/oracle" }
reqsign-tencent-cos = { version = "2.0.2", path = "services/tencent-cos" }

Expand Down
4 changes: 3 additions & 1 deletion reqsign/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ reqsign-aws-v4 = { workspace = true, optional = true }
reqsign-azure-storage = { workspace = true, optional = true }
reqsign-google = { workspace = true, optional = true }
reqsign-huaweicloud-obs = { workspace = true, optional = true }
reqsign-openstack-keystone = { workspace = true, optional = true }
reqsign-oracle = { workspace = true, optional = true }
reqsign-tencent-cos = { workspace = true, optional = true }

Expand All @@ -64,11 +65,12 @@ aws = ["dep:reqsign-aws-v4"]
azure = ["dep:reqsign-azure-storage"]
google = ["dep:reqsign-google"]
huaweicloud = ["dep:reqsign-huaweicloud-obs"]
openstack = ["dep:reqsign-openstack-keystone"]
oracle = ["dep:reqsign-oracle"]
tencent = ["dep:reqsign-tencent-cos"]

# Full feature set
full = ["aliyun", "aws", "azure", "google", "huaweicloud", "oracle", "tencent"]
full = ["aliyun", "aws", "azure", "google", "huaweicloud", "openstack", "oracle", "tencent"]

[dev-dependencies]
anyhow = "1"
Expand Down
3 changes: 3 additions & 0 deletions reqsign/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ pub mod google;
#[cfg(feature = "huaweicloud")]
pub mod huaweicloud;

#[cfg(feature = "openstack")]
pub mod openstack;

#[cfg(feature = "oracle")]
pub mod oracle;

Expand Down
65 changes: 65 additions & 0 deletions reqsign/src/openstack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//! OpenStack Keystone service support with convenience APIs
//!
//! This module provides OpenStack Keystone authentication functionality along
//! with convenience functions for common use cases.

// Re-export all OpenStack Keystone types
pub use reqsign_openstack_keystone::*;

#[cfg(feature = "default-context")]
use crate::{Signer, default_context};

/// Default OpenStack Signer type with commonly used components
#[cfg(feature = "default-context")]
pub type DefaultSigner = Signer<Credential>;

/// Create a default OpenStack signer with standard configuration
///
/// This function creates a signer with:
/// - Default context (with Tokio file reader, reqwest HTTP client, OS environment)
/// - Default credential provider (reads from `OPENSTACK_*` env vars)
/// - Request signer that inserts `X-Auth-Token` header
///
/// # Example
///
/// ```no_run
/// # #[tokio::main]
/// # async fn main() -> reqsign_core::Result<()> {
/// let signer = reqsign::openstack::default_signer();
///
/// let mut req = http::Request::builder()
/// .method("GET")
/// .uri("https://swift.example.com/v1/AUTH_test/container/object")
/// .body(())
/// .unwrap()
/// .into_parts()
/// .0;
///
/// signer.sign(&mut req, None).await?;
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "default-context")]
pub fn default_signer() -> DefaultSigner {
let ctx = default_context();
let provider = DefaultCredentialProvider::new();
let signer = RequestSigner;
Signer::new(ctx, provider, signer)
}
43 changes: 43 additions & 0 deletions services/openstack-keystone/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

[package]
name = "reqsign-openstack-keystone"
version = "2.0.0"

description = "OpenStack Keystone authentication implementation for reqsign."

edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true

[dependencies]
async-trait = { workspace = true }
http = { workspace = true }
log = { workspace = true }
reqsign-core = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }

[dev-dependencies]
dotenvy = { workspace = true }
env_logger = { workspace = true }
reqsign-file-read-tokio = { workspace = true }
reqsign-http-send-reqwest = { workspace = true }
reqwest = { workspace = true, features = ["default-tls"] }
tokio = { workspace = true, features = ["full"] }
Loading
Loading