Skip to content

Commit

Permalink
HMS-5292: Add list subs-as-features (#942)
Browse files Browse the repository at this point in the history
* HMS-5292: Add list subs-as-features

* add CertUser interface
  • Loading branch information
rverdile authored Jan 23, 2025
1 parent 5a7601d commit fdb732d
Show file tree
Hide file tree
Showing 13 changed files with 446 additions and 53 deletions.
3 changes: 3 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ packages:
github.com/content-services/content-sources-backend/pkg/candlepin_client:
interfaces:
CandlepinClient:
github.com/content-services/content-sources-backend/pkg/feature_service_client:
interfaces:
FeatureServiceClient:
github.com/content-services/content-sources-backend/pkg/cache:
interfaces:
Cache:
Expand Down
8 changes: 8 additions & 0 deletions configs/config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ clients:
rbac: 1m
pulp_content_path: 1h
subscription_check: 1h
feature_service:
server: https://subscription.stage.api.redhat.com/svcrest/subscription/v5
client_cert:
client_key:
ca_cert:
client_cert_path:
client_key_path:
ca_cert_path:

# Configuration for the mocks
mocks:
Expand Down
42 changes: 42 additions & 0 deletions deployments/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,26 @@ objects:
name: content-sources-candlepin
key: ca
optional: true
- name: CLIENTS_FEATURE_SERVICE_SERVER
value: ${CLIENTS_FEATURE_SERVICE_SERVER}
- name: CLIENTS_FEATURE_SERVICE_CLIENT_CERT
valueFrom:
secretKeyRef:
name: content-sources-candlepin
key: cert
optional: true
- name: CLIENTS_FEATURE_SERVICE_CLIENT_KEY
valueFrom:
secretKeyRef:
name: content-sources-candlepin
key: key
optional: true
- name: CLIENTS_FEATURE_SERVICE_CA_CERT
valueFrom:
secretKeyRef:
name: content-sources-candlepin
key: ca
optional: true
resources:
limits:
cpu: ${CPU_LIMIT_CS_WORKER}
Expand Down Expand Up @@ -316,6 +336,26 @@ objects:
name: content-sources-candlepin
key: ca
optional: true
- name: CLIENTS_FEATURE_SERVICE_SERVER
value: ${CLIENTS_FEATURE_SERVICE_SERVER}
- name: CLIENTS_FEATURE_SERVICE_CLIENT_CERT
valueFrom:
secretKeyRef:
name: content-sources-candlepin
key: cert
optional: true
- name: CLIENTS_FEATURE_SERVICE_CLIENT_KEY
valueFrom:
secretKeyRef:
name: content-sources-candlepin
key: key
optional: true
- name: CLIENTS_FEATURE_SERVICE_CA_CERT
valueFrom:
secretKeyRef:
name: content-sources-candlepin
key: ca
optional: true
resources:
limits:
cpu: ${CPU_LIMIT_CS_API}
Expand Down Expand Up @@ -862,5 +902,7 @@ parameters:
description: Number of task workers running within a single worker process
- name: CLIENTS_CANDLEPIN_SERVER
default: ''
- name: CLIENTS_FEATURE_SERVICE_SERVER
default: ''
- name: OPTIONS_FEATURE_FILTER
description: Comma separated list of features that determine which repos to import
4 changes: 4 additions & 0 deletions pkg/api/admin_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ type pulpProgressReportResponse struct {
Suffix zest.NullableString `json:"suffix,omitempty"`
}

type ListFeaturesResponse struct {
Features []string `json:"features"`
}

func (a *AdminTaskInfoCollectionResponse) SetMetadata(meta ResponseMetadata, links Links) {
a.Meta = meta
a.Links = links
Expand Down
42 changes: 1 addition & 41 deletions pkg/candlepin_client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ package candlepin_client

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"net/http"
"time"

caliri "github.com/content-services/caliri/release/v4"
"github.com/content-services/content-sources-backend/pkg/config"
Expand All @@ -34,43 +31,6 @@ func errorWithResponseBody(message string, httpResp *http.Response, err error) e
return err
}

func getHTTPClient() (http.Client, error) {
timeout := 90 * time.Second
transport := &http.Transport{ResponseHeaderTimeout: timeout}

certStr := config.Get().Clients.Candlepin.ClientCert
keyStr := config.Get().Clients.Candlepin.ClientKey
ca := config.Get().Clients.Candlepin.CACert
if certStr != "" {
cert, err := tls.X509KeyPair([]byte(certStr), []byte(keyStr))
if err != nil {
return http.Client{}, fmt.Errorf("could not load cert pair for candlepin %w", err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
if ca != "" {
pool, err := certPool(ca)
if err != nil {
return http.Client{}, err
}
tlsConfig.RootCAs = pool
}
transport.TLSClientConfig = tlsConfig
}
return http.Client{Transport: transport, Timeout: timeout}, nil
}

func certPool(caCert string) (*x509.CertPool, error) {
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM([]byte(caCert))
if !ok {
return nil, fmt.Errorf("could not parse candlepin ca cert")
}
return pool, nil
}

func getCorrelationId(ctx context.Context) string {
value := ctx.Value(config.ContextRequestIDKey{})
if value != nil {
Expand All @@ -89,7 +49,7 @@ func NewCandlepinClient() CandlepinClient {
}

func getCandlepinClient(ctx context.Context) (context.Context, *caliri.APIClient, error) {
httpClient, err := getHTTPClient()
httpClient, err := config.GetHTTPClient(&config.CandlepinCertUser{})
if err != nil {
return nil, nil, err
}
Expand Down
151 changes: 151 additions & 0 deletions pkg/config/certificates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package config

import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"time"
)

type CertUser interface {
ClientCert() string
ClientKey() string
CACert() string
ClientCertPath() string
ClientKeyPath() string
CACertPath() string
}

func GetHTTPClient(certUser CertUser) (http.Client, error) {
timeout := 90 * time.Second

var cert []byte
if certUser.ClientCert() != "" {
cert = []byte(certUser.ClientCert())
} else if certUser.ClientCertPath() != "" {
file, err := os.ReadFile(certUser.ClientCertPath())
if err != nil {
return http.Client{}, err
}
cert = file
}

var key []byte
if certUser.ClientKey() != "" {
key = []byte(certUser.ClientKey())
} else if certUser.ClientKeyPath() != "" {
file, err := os.ReadFile(certUser.ClientKeyPath())
if err != nil {
return http.Client{}, err
}
key = file
}

var caCert []byte
if certUser.CACert() != "" {
caCert = []byte(certUser.CACert())
} else if certUser.CACertPath() != "" {
file, err := os.ReadFile(certUser.CACertPath())
if err != nil {
return http.Client{}, err
}
caCert = file
}

transport, err := GetTransport(cert, key, caCert, timeout)
if err != nil {
return http.Client{}, fmt.Errorf("error creating http transport: %w", err)
}

return http.Client{Transport: transport, Timeout: timeout}, nil
}

func GetTransport(certBytes, keyBytes, caCertBytes []byte, timeout time.Duration) (*http.Transport, error) {
transport := &http.Transport{ResponseHeaderTimeout: timeout}

if certBytes != nil && keyBytes != nil {
cert, err := tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return transport, fmt.Errorf("could not load keypair: %w", err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}

if caCertBytes != nil {
pool, err := certPool(caCertBytes)
if err != nil {
return transport, err
}
tlsConfig.RootCAs = pool
}
transport.TLSClientConfig = tlsConfig
}
return transport, nil
}

func certPool(caCert []byte) (*x509.CertPool, error) {
pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM(caCert)
if !ok {
return nil, fmt.Errorf("could not parse ca cert")
}
return pool, nil
}

type FeatureServiceCertUser struct {
}

func (c *FeatureServiceCertUser) ClientCert() string {
return Get().Clients.FeatureService.ClientCert
}

func (c *FeatureServiceCertUser) ClientKey() string {
return Get().Clients.FeatureService.ClientKey
}

func (c *FeatureServiceCertUser) CACert() string {
return Get().Clients.FeatureService.CACert
}

func (c *FeatureServiceCertUser) CACertPath() string {
return Get().Clients.FeatureService.CACertPath
}

func (c *FeatureServiceCertUser) ClientCertPath() string {
return Get().Clients.FeatureService.ClientCertPath
}

func (c *FeatureServiceCertUser) ClientKeyPath() string {
return Get().Clients.FeatureService.ClientKeyPath
}

type CandlepinCertUser struct {
}

func (c *CandlepinCertUser) ClientCert() string {
return Get().Clients.Candlepin.ClientCert
}

func (c *CandlepinCertUser) ClientKey() string {
return Get().Clients.Candlepin.ClientKey
}

func (c *CandlepinCertUser) CACert() string {
return Get().Clients.Candlepin.CACert
}

func (c *CandlepinCertUser) CACertPath() string {
return ""
}

func (c *CandlepinCertUser) ClientCertPath() string {
return ""
}

func (c *CandlepinCertUser) ClientKeyPath() string {
return ""
}
31 changes: 25 additions & 6 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ type Configuration struct {
}

type Clients struct {
RbacEnabled bool `mapstructure:"rbac_enabled"`
RbacBaseUrl string `mapstructure:"rbac_base_url"`
RbacTimeout int `mapstructure:"rbac_timeout"`
Pulp Pulp `mapstructure:"pulp"`
Redis Redis `mapstructure:"redis"`
Candlepin Candlepin `mapstructure:"candlepin"`
RbacEnabled bool `mapstructure:"rbac_enabled"`
RbacBaseUrl string `mapstructure:"rbac_base_url"`
RbacTimeout int `mapstructure:"rbac_timeout"`
Pulp Pulp `mapstructure:"pulp"`
Redis Redis `mapstructure:"redis"`
Candlepin Candlepin `mapstructure:"candlepin"`
FeatureService FeatureService `mapstructure:"feature_service"`
}

type Mocks struct {
Expand Down Expand Up @@ -96,6 +97,16 @@ type Candlepin struct {
DevelOrg bool `mapstructure:"devel_org"` // For use only in dev envs
}

type FeatureService struct {
Server string
ClientCert string `mapstructure:"client_cert"`
ClientKey string `mapstructure:"client_key"`
CACert string `mapstructure:"ca_cert"`
ClientCertPath string `mapstructure:"client_cert_path"`
ClientKeyPath string `mapstructure:"client_key_path"`
CACertPath string `mapstructure:"ca_cert_path"`
}

const RepoClowderBucketName = "content-sources-central-pulp-s3"

type ObjectStore struct {
Expand Down Expand Up @@ -296,6 +307,14 @@ func setDefaults(v *viper.Viper) {
v.SetDefault("clients.redis.expiration.pulp_content_path", 1*time.Hour)
v.SetDefault("clients.redis.expiration.subscription_check", 1*time.Hour)

v.SetDefault("clients.feature_service.server", "")
v.SetDefault("clients.feature_service.client_cert", "")
v.SetDefault("clients.feature_service.client_key", "")
v.SetDefault("clients.feature_service.ca_cert", "")
v.SetDefault("clients.feature_service.client_cert_path", "")
v.SetDefault("clients.feature_service.client_key_path", "")
v.SetDefault("clients.feature_service.ca_cert_path", "")

v.SetDefault("tasking.heartbeat", 1*time.Minute)
v.SetDefault("tasking.worker_count", 3)
v.SetDefault("tasking.pgx_logging", true)
Expand Down
24 changes: 24 additions & 0 deletions pkg/feature_service_client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package feature_service_client

import (
"context"
"net/http"

"github.com/content-services/content-sources-backend/pkg/config"
)

type FeatureServiceClient interface {
ListFeatures(ctx context.Context) (features FeaturesResponse, statusCode int, err error)
}

type featureServiceImpl struct {
client http.Client
}

func NewFeatureServiceClient() (FeatureServiceClient, error) {
httpClient, err := config.GetHTTPClient(&config.FeatureServiceCertUser{})
if err != nil {
return nil, err
}
return featureServiceImpl{client: httpClient}, nil
}
Loading

0 comments on commit fdb732d

Please sign in to comment.