Skip to content

Commit

Permalink
Merge branch 'v3' of https://github.com/canonical/jimm into add-micro…
Browse files Browse the repository at this point in the history
…k8s-controller
  • Loading branch information
ale8k committed Jun 11, 2024
2 parents 3fe239b + 4618aec commit 5220a29
Show file tree
Hide file tree
Showing 67 changed files with 1,281 additions and 807 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
build_test:
name: Build and Test
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
with:
Expand Down
8 changes: 6 additions & 2 deletions api/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type AddControllerRequest struct {
// themselves are migrated.
PublicAddress string `json:"public-address,omitempty"`

// TLSHostname is the hostname used for TLS verification.
TLSHostname string `json:"tls-hostname,omitempty"`

// APIAddresses contains the currently known API addresses for the
// controller.
APIAddresses []string `json:"api-addresses,omitempty"`
Expand Down Expand Up @@ -347,8 +350,9 @@ type ListRelationshipTuplesRequest struct {

// ListRelationshipTuplesResponse holds the response of the ListRelationshipTuples method.
type ListRelationshipTuplesResponse struct {
Tuples []RelationshipTuple `json:"tuples,omitempty"`
ContinuationToken string `json:"continuation_token,omitempty"`
Tuples []RelationshipTuple `json:"tuples,omitempty" yaml:"tuples,omitempty"`
Errors []string `json:"errors,omitempty" yaml:"errors,omitempty"`
ContinuationToken string `json:"continuation_token,omitempty" yaml:"continuation_token,omitempty"`
}

// CrossModelQueryRequest holds the parameters to perform a cross model query against
Expand Down
6 changes: 0 additions & 6 deletions charms/jimm/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,3 @@ options:
description: |
The max age for the session cookies in seconds, on subsequent logins, the session instance
extended by this amount.
final-redirect-url:
type: string
default: ""
description: |
The final redirect URL for JIMM to redirect to when completing a browser based
login. This should be your dashboard.
1 change: 0 additions & 1 deletion charms/jimm/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ def _on_config_changed(self, _):
"session_expiry_duration": self.config.get("session-expiry-duration"),
"secure_session_cookies": self.config.get("secure-session-cookies"),
"session_cookie_max_age": self.config.get("session-cookie-max-age"),
"final_redirect_url": self.config.get("final-redirect-url"),
}

self.oauth.update_client_config(client_config=self._oauth_client_config)
Expand Down
2 changes: 1 addition & 1 deletion charms/jimm/templates/jimm.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
JIMM_ADMINS={{admins}}
{% if dashboard_location %}
JIMM_DASHBOARD_LOCATION={{dashboard_location}}
JIMM_DASHBOARD_FINAL_REDIRECT_URL={{dashboard_location}}
{% endif %}
{% if dns_name %}
JIMM_DNS_NAME={{dns_name}}
Expand All @@ -20,4 +21,3 @@ JIMM_MACAROON_EXPIRY_DURATION={{macaroon_expiry_duration}}
JIMM_ACCESS_TOKEN_EXPIRY_DURATION={{session_expiry_duration}}
JIMM_SECURE_SESSION_COOKIES={{secure_session_cookies}}
JIMM_SESSION_COOKIE_MAX_AGE={{session_cookie_max_age}}
JIMM_DASHBOARD_FINAL_REDIRECT_URL={{final_redirect_url}}
186 changes: 70 additions & 116 deletions charms/jimm/tests/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,40 +172,25 @@ def test_config_changed(self):
"macaroon-expiry-duration": "48h",
"secure-session-cookies": True,
"session-cookie-max-age": 86400,
"final-redirect-url": "",
}
)
self.assertTrue(os.path.exists(config_file))
with open(config_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
os.unlink(config_file)
self.assertEqual(len(lines), 19)
self.assertEqual(lines[0].strip(), "JIMM_ADMINS=user1 user2 group1")
self.assertEqual(
lines[2].strip(),
"JIMM_DASHBOARD_LOCATION=https://jaas.ai/models",
)
self.assertEqual(lines[5].strip(), "JIMM_DNS_NAME=" + "jimm.example.com")
self.assertEqual(lines[7].strip(), "JIMM_LOG_LEVEL=debug")
self.assertEqual(lines[8].strip(), "JIMM_UUID=caaa4ba4-e2b5-40dd-9bf3-2bd26d6e17aa")
self.assertEqual(
lines[9].strip(),
"BAKERY_PRIVATE_KEY=ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=",
)
self.assertEqual(
lines[10].strip(),
"BAKERY_PUBLIC_KEY=izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=",
)
self.assertEqual(
lines[11].strip(),
"JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS=10",
)
self.assertEqual(
lines[12].strip(),
"JIMM_JWT_EXPIRY=10m",
)
self.assertEqual(lines[14].strip(), "JIMM_MACAROON_EXPIRY_DURATION=48h")
self.assertEqual(lines[15].strip(), "JIMM_ACCESS_TOKEN_EXPIRY_DURATION=6h")
self.assertIn("JIMM_ADMINS=user1 user2 group1", lines)
self.assertIn("JIMM_DASHBOARD_LOCATION=https://jaas.ai/models", lines)
self.assertIn("JIMM_DASHBOARD_FINAL_REDIRECT_URL=https://jaas.ai/models", lines)
self.assertIn("JIMM_DNS_NAME=jimm.example.com", lines)
self.assertIn("JIMM_LOG_LEVEL=debug", lines)
self.assertIn("JIMM_UUID=caaa4ba4-e2b5-40dd-9bf3-2bd26d6e17aa", lines)
self.assertIn("BAKERY_PRIVATE_KEY=ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=", lines)
self.assertIn("BAKERY_PUBLIC_KEY=izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=", lines)
self.assertIn("JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS=10", lines)
self.assertIn("JIMM_JWT_EXPIRY=10m", lines)
self.assertIn("JIMM_MACAROON_EXPIRY_DURATION=48h", lines)
self.assertIn("JIMM_ACCESS_TOKEN_EXPIRY_DURATION=6h", lines)

def test_config_changed_redirect_to_dashboard(self):
config_file = os.path.join(self.harness.charm.charm_dir, "juju-jimm.env")
Expand All @@ -222,39 +207,24 @@ def test_config_changed_redirect_to_dashboard(self):
"macaroon-expiry-duration": "48h",
"secure-session-cookies": True,
"session-cookie-max-age": 86400,
"final-redirect-url": "",
}
)
self.assertTrue(os.path.exists(config_file))
with open(config_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
os.unlink(config_file)
self.assertEqual(len(lines), 19)
self.assertEqual(lines[0].strip(), "JIMM_ADMINS=user1 user2 group1")
self.assertEqual(
lines[2].strip(),
"JIMM_DASHBOARD_LOCATION=https://test.jaas.ai/models",
)
self.assertEqual(lines[5].strip(), "JIMM_DNS_NAME=" + "jimm.example.com")
self.assertEqual(lines[7].strip(), "JIMM_LOG_LEVEL=debug")
self.assertEqual(lines[8].strip(), "JIMM_UUID=caaa4ba4-e2b5-40dd-9bf3-2bd26d6e17aa")
self.assertEqual(
lines[9].strip(),
"BAKERY_PRIVATE_KEY=ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=",
)
self.assertEqual(
lines[10].strip(),
"BAKERY_PUBLIC_KEY=izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=",
)
self.assertEqual(
lines[11].strip(),
"JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS=10",
)
self.assertEqual(
lines[12].strip(),
"JIMM_JWT_EXPIRY=5m",
)
self.assertEqual(lines[14].strip(), "JIMM_MACAROON_EXPIRY_DURATION=48h")
self.assertIn("JIMM_ADMINS=user1 user2 group1", lines)
self.assertIn("JIMM_DASHBOARD_LOCATION=https://test.jaas.ai/models", lines)
self.assertIn("JIMM_DASHBOARD_FINAL_REDIRECT_URL=https://test.jaas.ai/models", lines)
self.assertIn("JIMM_DNS_NAME=" + "jimm.example.com", lines)
self.assertIn("JIMM_LOG_LEVEL=debug", lines)
self.assertIn("JIMM_UUID=caaa4ba4-e2b5-40dd-9bf3-2bd26d6e17aa", lines)
self.assertIn("BAKERY_PRIVATE_KEY=ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=", lines)
self.assertIn("BAKERY_PUBLIC_KEY=izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=", lines)
self.assertIn("JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS=10", lines)
self.assertIn("JIMM_JWT_EXPIRY=5m", lines)
self.assertIn("JIMM_MACAROON_EXPIRY_DURATION=48h", lines)

def test_config_changed_ready(self):
config_file = os.path.join(self.harness.charm.charm_dir, "juju-jimm.env")
Expand All @@ -270,51 +240,35 @@ def test_config_changed_ready(self):
"macaroon-expiry-duration": "48h",
"secure-session-cookies": True,
"session-cookie-max-age": 86400,
"final-redirect-url": "",
}
)
self.assertTrue(os.path.exists(config_file))
with open(config_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
os.unlink(config_file)
self.assertEqual(len(lines), 17)
self.assertEqual(lines[0].strip(), "JIMM_ADMINS=user1 user2 group1")
self.assertEqual(
lines[2].strip(),
"JIMM_DASHBOARD_LOCATION=https://jaas.ai/models",
)
self.assertEqual(lines[5].strip(), "JIMM_LOG_LEVEL=info")
self.assertEqual(lines[6].strip(), "JIMM_UUID=caaa4ba4-e2b5-40dd-9bf3-2bd26d6e17aa")
self.assertEqual(
lines[7].strip(),
"BAKERY_PRIVATE_KEY=ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=",
)
self.assertEqual(
lines[8].strip(),
"BAKERY_PUBLIC_KEY=izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=",
)
self.assertEqual(
lines[9].strip(),
"JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS=10",
)
self.assertEqual(
lines[10].strip(),
"JIMM_JWT_EXPIRY=5m",
)
self.assertEqual(lines[12].strip(), "JIMM_MACAROON_EXPIRY_DURATION=48h")
self.assertIn("JIMM_ADMINS=user1 user2 group1", lines)
self.assertIn("JIMM_DASHBOARD_LOCATION=https://jaas.ai/models", lines)
self.assertIn("JIMM_LOG_LEVEL=info", lines)
self.assertIn("JIMM_UUID=caaa4ba4-e2b5-40dd-9bf3-2bd26d6e17aa", lines)
self.assertIn("BAKERY_PRIVATE_KEY=ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=", lines)
self.assertIn("BAKERY_PUBLIC_KEY=izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=", lines)
self.assertIn("JIMM_AUDIT_LOG_RETENTION_PERIOD_IN_DAYS=10", lines)
self.assertIn("JIMM_JWT_EXPIRY=5m", lines)
self.assertIn("JIMM_MACAROON_EXPIRY_DURATION=48h", lines)

def test_leader_elected(self):
leader_file = os.path.join(self.harness.charm.charm_dir, "juju-jimm-leader.env")
self.harness.charm.on.leader_elected.emit()
with open(leader_file) as f:
lines = f.readlines()
self.assertEqual(lines[0].strip(), "JIMM_WATCH_CONTROLLERS=")
self.assertEqual(lines[1].strip(), "JIMM_ENABLE_JWKS_ROTATOR=")
lines = [line.strip() for line in f.readlines()]
self.assertIn("JIMM_WATCH_CONTROLLERS=", lines)
self.assertIn("JIMM_ENABLE_JWKS_ROTATOR=", lines)
self.harness.set_leader(True)
with open(leader_file) as f:
lines = f.readlines()
self.assertEqual(lines[0].strip(), "JIMM_WATCH_CONTROLLERS=1")
self.assertEqual(lines[1].strip(), "JIMM_ENABLE_JWKS_ROTATOR=1")
lines = [line.strip() for line in f.readlines()]
self.assertIn("JIMM_WATCH_CONTROLLERS=1", lines)
self.assertIn("JIMM_ENABLE_JWKS_ROTATOR=1", lines)

def test_leader_elected_ready(self):
leader_file = os.path.join(self.harness.charm.charm_dir, "juju-jimm-leader.env")
Expand All @@ -324,14 +278,14 @@ def test_leader_elected_ready(self):
f.write("test")
self.harness.charm.on.leader_elected.emit()
with open(leader_file) as f:
lines = f.readlines()
self.assertEqual(lines[0].strip(), "JIMM_WATCH_CONTROLLERS=")
lines = [line.strip() for line in f.readlines()]
self.assertIn("JIMM_WATCH_CONTROLLERS=", lines)
self.harness.set_leader(True)
self.add_oauth_relation()
self.add_openfga_relation()
with open(leader_file) as f:
lines = f.readlines()
self.assertEqual(lines[0].strip(), "JIMM_WATCH_CONTROLLERS=1")
lines = [line.strip() for line in f.readlines()]
self.assertIn("JIMM_WATCH_CONTROLLERS=1", lines)
self.harness.charm._systemctl.assert_has_calls(
(
call("is-enabled", self.harness.charm.service),
Expand All @@ -353,14 +307,14 @@ def test_database_relation_changed(self):
},
)
with open(db_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0].strip(), "JIMM_DSN=postgresql://some-username:[email protected]/jimm")
self.assertIn("JIMM_DSN=postgresql://some-username:[email protected]/jimm", lines)
self.harness.update_relation_data(id, "postgresql", {})
with open(db_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0].strip(), "JIMM_DSN=postgresql://some-username:[email protected]/jimm")
self.assertIn("JIMM_DSN=postgresql://some-username:[email protected]/jimm", lines)

def test_database_relation_changed_ready(self):
db_file = os.path.join(self.harness.charm.charm_dir, "juju-jimm-db.env")
Expand All @@ -381,14 +335,14 @@ def test_database_relation_changed_ready(self):
},
)
with open(db_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0].strip(), "JIMM_DSN=postgresql://some-username:[email protected]/jimm")
self.assertIn("JIMM_DSN=postgresql://some-username:[email protected]/jimm", lines)
self.harness.update_relation_data(id, "postgresql", {})
with open(db_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0].strip(), "JIMM_DSN=postgresql://some-username:[email protected]/jimm")
self.assertIn("JIMM_DSN=postgresql://some-username:[email protected]/jimm", lines)
self.harness.charm._systemctl.assert_has_calls(
(
call("is-enabled", self.harness.charm.service),
Expand Down Expand Up @@ -447,14 +401,14 @@ def test_vault_relation_changed(self):
{"data": {"role_id": "test-role-id", "secret_id": "test-secret"}},
)
with open(self.harness.charm._env_filename("vault")) as f:
lines = f.readlines()
self.assertEqual(lines[0].strip(), "VAULT_ADDR=http://vault:8200")
self.assertEqual(lines[1].strip(), "VAULT_PATH=charm-jimm-creds")
lines = [line.strip() for line in f.readlines()]
self.assertIn("VAULT_ADDR=http://vault:8200", lines)
self.assertIn("VAULT_PATH=charm-jimm-creds", lines)
self.assertEqual(
lines[2].strip(),
"VAULT_SECRET_FILE={}".format(self.harness.charm._vault_secret_filename),
)
self.assertEqual(lines[3].strip(), "VAULT_AUTH_PATH=/auth/approle/login")
self.assertIn("VAULT_AUTH_PATH=/auth/approle/login", lines)

def test_stop(self):
self.harness.charm.on.stop.emit()
Expand Down Expand Up @@ -540,13 +494,13 @@ def test_openfga_relation_changed(self):
self.add_openfga_relation()

with open(self.harness.charm._env_filename("openfga")) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]

self.assertEqual(lines[0].strip(), "OPENFGA_HOST=openfga.localhost")
self.assertEqual(lines[1].strip(), "OPENFGA_PORT=8080")
self.assertEqual(lines[2].strip(), "OPENFGA_SCHEME=http")
self.assertEqual(lines[3].strip(), "OPENFGA_STORE=fake-store-id")
self.assertEqual(lines[4].strip(), "OPENFGA_TOKEN=fake-token")
self.assertIn("OPENFGA_HOST=openfga.localhost", lines)
self.assertIn("OPENFGA_PORT=8080", lines)
self.assertIn("OPENFGA_SCHEME=http", lines)
self.assertIn("OPENFGA_STORE=fake-store-id", lines)
self.assertIn("OPENFGA_TOKEN=fake-token", lines)

def test_insecure_secret_storage(self):
"""Test that the flag for insecure secret storage is only generated when explicitly requested."""
Expand All @@ -563,14 +517,14 @@ def test_insecure_secret_storage(self):
)
self.assertTrue(os.path.exists(config_file))
with open(config_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
os.unlink(config_file)
self.assertEqual(len(lines), 19)
self.assertEqual(len([match for match in lines if "INSECURE_SECRET_STORAGE" in match]), 0)
self.harness.update_config({"postgres-secret-storage": True})
self.assertTrue(os.path.exists(config_file))
with open(config_file) as f:
lines = f.readlines()
lines = [line.strip() for line in f.readlines()]
os.unlink(config_file)
self.assertEqual(len(lines), 21)
self.assertEqual(len([match for match in lines if "INSECURE_SECRET_STORAGE" in match]), 1)
Expand All @@ -580,11 +534,11 @@ def test_oauth_relation_changed(self):
self.add_oauth_relation()

with open(self.harness.charm._env_filename("oauth")) as f:
lines = f.readlines()
self.assertEqual(lines[0].strip(), "JIMM_OAUTH_ISSUER_URL=https://example.oidc.com")
self.assertEqual(lines[1].strip(), "JIMM_OAUTH_CLIENT_ID=jimm_client_id")
self.assertEqual(lines[2].strip(), "JIMM_OAUTH_CLIENT_SECRET=test-secret")
self.assertEqual(lines[3].strip(), "JIMM_OAUTH_SCOPES=openid profile email phone")
lines = [line.strip() for line in f.readlines()]
self.assertIn("JIMM_OAUTH_ISSUER_URL=https://example.oidc.com", lines)
self.assertIn("JIMM_OAUTH_CLIENT_ID=jimm_client_id", lines)
self.assertIn("JIMM_OAUTH_CLIENT_SECRET=test-secret", lines)
self.assertIn("JIMM_OAUTH_SCOPES=openid profile email phone", lines)


class VersionHTTPRequestHandler(BaseHTTPRequestHandler):
Expand Down
Loading

0 comments on commit 5220a29

Please sign in to comment.