huatuo/internal/pod/container.go

208 lines
5.5 KiB
Go

// Copyright 2025 The HuaTuo Authors
//
// Licensed 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 pod
import (
"errors"
"fmt"
"sync"
"syscall"
"time"
"huatuo-bamai/internal/log"
)
var (
// all containers, map: ContainerID -> *Container
containers = map[string]*Container{}
// updated
lastUpdatedAt = time.Now()
updatedStep = 5 * time.Second
updatedLock sync.Mutex
)
// Container object
type Container struct {
ID string `json:"id"`
Name string `json:"name"`
Hostname string `json:"hostname"`
Type ContainerType `json:"type"`
Qos ContainerQos `json:"qos"`
IPAddress string `json:"ip_address"`
NetNamespaceInode uint64 `json:"net_namespace_inode"`
InitPid int `json:"init_pid"` // the pid-1 of container
CgroupSuffix string `json:"cgroup_suffix"`
CSS map[string]uint64 `json:"css"` // map: Name -> Address
StartedAt time.Time `json:"started_at"` // started time
SyncedAt time.Time `json:"synced_at"` // synced time
lifeResouces map[string]any
Labels map[string]any `json:"labels"` // custom labels
}
func (c *Container) String() string {
return fmt.Sprintf("%s:%s/%s/%s:%s/%s", c.ID, c.Hostname, c.Name, c.Type, c.Qos, c.IPAddress)
}
// LifeResouces returns the life resouces of container.
func (c *Container) LifeResouces(key string) any {
return c.lifeResouces[key]
}
// LabelHostNamespace returns namespace label
func (c *Container) LabelHostNamespace() string {
return c.Labels[labelHostNamespace].(string)
}
// getContainers returns the containers by type and level.
func getContainers(typeMask ContainerType, minLevel ContainerQos) (map[string]*Container, error) {
updatedLock.Lock()
defer updatedLock.Unlock()
res := make(map[string]*Container)
if time.Since(lastUpdatedAt) > updatedStep {
if err := kubeletSyncContainers(); err != nil {
if errors.Is(err, syscall.ECONNREFUSED) { // ignore error of no connections
log.Debugf("failed to sync containers by ECONNREFUSED, err: %v", err)
return res, nil
}
return res, err
}
lastUpdatedAt = time.Now()
}
log.Debugf("sync latest containers: %+v", containers)
for _, c := range containers {
// check Type
if c.Type&typeMask == 0 {
continue
}
// check Level
if c.Qos < minLevel {
continue
}
res[c.ID] = c
}
return res, nil
}
// GetContainersByType returns the containers by type.
func GetContainersByType(typeMask ContainerType) (map[string]*Container, error) {
return getContainers(typeMask, ContainerQosLevelMin)
}
// GetNormalContainers returns the normal containers.
func GetNormalContainers() (map[string]*Container, error) {
return GetContainersByType(ContainerTypeNormal)
}
// GetNormalAndSidecarContainers returns the normal and sidecar containers.
func GetNormalAndSidecarContainers() (map[string]*Container, error) {
return GetContainersByType(ContainerTypeNormal | ContainerTypeSidecar)
}
// GetAllContainers returns all containers.
func GetAllContainers() (map[string]*Container, error) {
return getContainers(ContainerTypeAll, ContainerQosLevelMin)
}
// GetContainerByID returns the special container by id.
func GetContainerByID(id string) (*Container, error) {
all, err := GetAllContainers()
if err != nil {
return nil, err
}
if c, ok := all[id]; ok {
return c, nil
}
return nil, nil
}
// GetContainerByIPAddress returns the special container by the container ip address.
func GetContainerByIPAddress(ip string) (*Container, error) {
// only for normal
all, err := GetNormalContainers()
if err != nil {
return nil, err
}
for _, c := range all {
if c.IPAddress == ip {
return c, nil
}
}
return nil, nil
}
// GetContainerByNetNamespaceInode returns the special container by the net namespace inode.
func GetContainerByNetNamespaceInode(inode uint64) (*Container, error) {
// only for normal
all, err := GetNormalContainers()
if err != nil {
return nil, err
}
for _, c := range all {
if c.NetNamespaceInode == inode {
return c, nil
}
}
return nil, nil
}
// GetContainerByCSS returns the special container by the css address.
func GetContainerByCSS(css uint64, subsys string) (*Container, error) {
all, err := GetAllContainers()
if err != nil {
return nil, err
}
for _, c := range all {
if addr, ok := c.CSS[subsys]; ok {
if addr == css {
return c, nil
}
}
}
return nil, nil
}
// GetCSSToContainerID Build mapping from css address to container id
// Usage: return_val = GetCSSToContainerID('cpu')
//
// container_id = return_val[0xffffffffc0601000]
func GetCSSToContainerID(subsys string) (map[uint64]string, error) {
containers, err := GetAllContainers()
if err != nil {
return nil, err
}
cssToContainerMap := make(map[uint64]string)
for _, container := range containers {
if addr, ok := container.CSS[subsys]; ok {
cssToContainerMap[addr] = container.ID
}
}
return cssToContainerMap, nil
}