metadata.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package openshift // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders/openshift"
  4. import (
  5. "context"
  6. "crypto/tls"
  7. "encoding/json"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "strings"
  12. )
  13. // Provider gets cluster metadata from Openshift.
  14. type Provider interface {
  15. K8SClusterVersion(context.Context) (string, error)
  16. OpenShiftClusterVersion(context.Context) (string, error)
  17. Infrastructure(context.Context) (*InfrastructureAPIResponse, error)
  18. }
  19. // NewProvider creates a new metadata provider.
  20. func NewProvider(address, token string, tlsCfg *tls.Config) Provider {
  21. cl := &http.Client{}
  22. if tlsCfg != nil {
  23. transport := http.DefaultTransport.(*http.Transport).Clone()
  24. transport.TLSClientConfig = tlsCfg
  25. cl.Transport = transport
  26. }
  27. return &openshiftProvider{
  28. address: address,
  29. token: token,
  30. client: cl,
  31. }
  32. }
  33. type openshiftProvider struct {
  34. client *http.Client
  35. address string
  36. token string
  37. }
  38. func (o *openshiftProvider) makeOCPRequest(ctx context.Context, endpoint, target string) (*http.Request, error) {
  39. addr := fmt.Sprintf("%s/apis/config.openshift.io/v1/%s/%s/status", o.address, endpoint, target)
  40. req, err := http.NewRequestWithContext(ctx, http.MethodGet, addr, nil)
  41. if err != nil {
  42. return nil, err
  43. }
  44. req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", o.token))
  45. return req, nil
  46. }
  47. // OpenShiftClusterVersion requests the ClusterVersion from the openshift api.
  48. func (o *openshiftProvider) OpenShiftClusterVersion(ctx context.Context) (string, error) {
  49. req, err := o.makeOCPRequest(ctx, "clusterversions", "version")
  50. if err != nil {
  51. return "", err
  52. }
  53. resp, err := o.client.Do(req)
  54. if err != nil {
  55. return "", err
  56. }
  57. res := ocpClusterVersionAPIResponse{}
  58. if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
  59. return "", err
  60. }
  61. return res.Status.Desired.Version, nil
  62. }
  63. // ClusterVersion requests Infrastructure details from the openshift api.
  64. func (o *openshiftProvider) Infrastructure(ctx context.Context) (*InfrastructureAPIResponse, error) {
  65. req, err := o.makeOCPRequest(ctx, "infrastructures", "cluster")
  66. if err != nil {
  67. return nil, err
  68. }
  69. resp, err := o.client.Do(req)
  70. if err != nil {
  71. return nil, err
  72. }
  73. data, err := io.ReadAll(resp.Body)
  74. if err != nil {
  75. return nil, err
  76. }
  77. res := &InfrastructureAPIResponse{}
  78. if err := json.Unmarshal(data, res); err != nil {
  79. return nil, fmt.Errorf("unable to unmarshal response, err: %w, response: %s",
  80. err, string(data),
  81. )
  82. }
  83. return res, nil
  84. }
  85. // K8SClusterVersion requests the ClusterVersion from the kubernetes api.
  86. func (o *openshiftProvider) K8SClusterVersion(ctx context.Context) (string, error) {
  87. addr := fmt.Sprintf("%s/version", o.address)
  88. req, err := http.NewRequestWithContext(ctx, http.MethodGet, addr, nil)
  89. if err != nil {
  90. return "", err
  91. }
  92. req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", o.token))
  93. resp, err := o.client.Do(req)
  94. if err != nil {
  95. return "", err
  96. }
  97. res := k8sClusterVersionAPIResponse{}
  98. if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
  99. return "", err
  100. }
  101. version := res.GitVersion
  102. if strings.Contains(version, "+") {
  103. version = strings.Split(version, "+")[0]
  104. }
  105. return version, nil
  106. }
  107. type ocpClusterVersionAPIResponse struct {
  108. Status struct {
  109. Desired struct {
  110. Version string `json:"version"`
  111. } `json:"desired"`
  112. } `json:"status"`
  113. }
  114. // InfrastructureAPIResponse from OpenShift API.
  115. type InfrastructureAPIResponse struct {
  116. Status InfrastructureStatus `json:"status"`
  117. }
  118. // InfrastructureStatus holds cluster-wide information about Infrastructure.
  119. // https://docs.openshift.com/container-platform/4.11/rest_api/config_apis/infrastructure-config-openshift-io-v1.html#apisconfig-openshift-iov1infrastructuresnamestatus
  120. type InfrastructureStatus struct {
  121. // ControlPlaneTopology expresses the expectations for operands that normally
  122. // run on control nodes. The default is 'HighlyAvailable', which represents
  123. // the behavior operators have in a "normal" cluster. The 'SingleReplica' mode
  124. // will be used in single-node deployments and the operators should not
  125. // configure the operand for highly-available operation The 'External' mode
  126. // indicates that the control plane is hosted externally to the cluster and
  127. // that its components are not visible within the cluster.
  128. ControlPlaneTopology string `json:"controlPlaneTopology"`
  129. // InfrastructureName uniquely identifies a cluster with a human friendly
  130. // name. Once set it should not be changed. Must be of max length 27 and must
  131. // have only alphanumeric or hyphen characters.
  132. InfrastructureName string `json:"infrastructureName"`
  133. // InfrastructureTopology expresses the expectations for infrastructure
  134. // services that do not run on control plane nodes, usually indicated by a
  135. // node selector for a role value other than master. The default is
  136. // 'HighlyAvailable', which represents the behavior operators have in a
  137. // "normal" cluster. The 'SingleReplica' mode will be used in single-node
  138. // deployments and the operators should not configure the operand for
  139. // highly-available operation.
  140. InfrastructureTopology string `json:"infrastructureTopology"`
  141. // PlatformStatus holds status information specific to the underlying
  142. // infrastructure provider.
  143. PlatformStatus InfrastructurePlatformStatus `json:"platformStatus"`
  144. }
  145. // InfrastructurePlatformStatus reported by the OpenShift API.
  146. type InfrastructurePlatformStatus struct {
  147. Aws InfrastructureStatusAWS `json:"aws"`
  148. Azure InfrastructureStatusAzure `json:"azure"`
  149. Baremetal struct{} `json:"baremetal"`
  150. GCP InfrastructureStatusGCP `json:"gcp"`
  151. IBMCloud InfrastructureStatusIBMCloud `json:"ibmcloud"`
  152. OpenStack InfrastructureStatusOpenStack `json:"openstack"`
  153. OVirt struct{} `json:"ovirt"`
  154. VSphere struct{} `json:"vsphere"`
  155. Type string `json:"type"`
  156. }
  157. // InfrastructureStatusAWS reported by the OpenShift API.
  158. type InfrastructureStatusAWS struct {
  159. // Region holds the default AWS region for new AWS resources created by the
  160. // cluster.
  161. Region string `json:"region"`
  162. }
  163. // InfrastructureStatusAzure reported by the OpenShift API.
  164. type InfrastructureStatusAzure struct {
  165. // CloudName is the name of the Azure cloud environment which can be used to
  166. // configure the Azure SDK with the appropriate Azure API endpoints. If empty,
  167. // the value is equal to AzurePublicCloud.
  168. CloudName string `json:"cloudName"`
  169. }
  170. // InfrastructureStatusGCP reported by the OpenShift API.
  171. type InfrastructureStatusGCP struct {
  172. // Region holds the region for new GCP resources created for the cluster.
  173. Region string `json:"region"`
  174. }
  175. // InfrastructureStatusIBMCloud reported by the OpenShift API.
  176. type InfrastructureStatusIBMCloud struct {
  177. // Location is where the cluster has been deployed.
  178. Location string `json:"location"`
  179. }
  180. // InfrastructureStatusOpenStack reported by the OpenShift API.
  181. type InfrastructureStatusOpenStack struct {
  182. // CloudName is the name of the desired OpenStack cloud in the client
  183. // configuration file (clouds.yaml).
  184. CloudName string `json:"cloudName"`
  185. }
  186. // k8sClusterVersionAPIResponse of OpenShift.
  187. // https://docs.openshift.com/container-platform/4.11/rest_api/config_apis/clusterversion-config-openshift-io-v1.html#apisconfig-openshift-iov1clusterversionsnamestatus
  188. type k8sClusterVersionAPIResponse struct {
  189. GitVersion string `json:"gitVersion"`
  190. }