go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/kubelet.go (about)

     1  // copyright: 2019, Dominik Richter and Christoph Hartmann
     2  // author: Dominik Richter
     3  // author: Christoph Hartmann
     4  
     5  package resources
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"github.com/spf13/afero"
    13  	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
    14  
    15  	"sigs.k8s.io/yaml"
    16  
    17  	"go.mondoo.com/cnquery/llx"
    18  	"go.mondoo.com/cnquery/providers-sdk/v1/plugin"
    19  	"go.mondoo.com/cnquery/providers-sdk/v1/util/convert"
    20  	"go.mondoo.com/cnquery/providers/os/connection/shared"
    21  	"go.mondoo.com/cnquery/types"
    22  )
    23  
    24  const defaultKubeletConfig = "/var/lib/kubelet/config.yaml"
    25  
    26  func initKubelet(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) {
    27  	if len(args) > 0 {
    28  		return args, nil, nil
    29  	}
    30  
    31  	p, err := getKubeletProcess(runtime)
    32  	if err != nil {
    33  		return nil, nil, err
    34  	}
    35  	args["process"] = llx.ResourceData(p, "process")
    36  
    37  	kubeletFlagsData := p.GetFlags()
    38  	if kubeletFlagsData.Error != nil {
    39  		return nil, nil, err
    40  	}
    41  	kubeletFlags := kubeletFlagsData.Data
    42  
    43  	// Check kubelet for "--config" flag and set path to config file accordingly
    44  	configFilePath := defaultKubeletConfig
    45  	if kubeletConfigFilePath, ok := kubeletFlags["config"]; ok {
    46  		path, ok := kubeletConfigFilePath.(string)
    47  		if !ok {
    48  			return nil, nil, errors.New("wrong type for value of '--config' flag, it must be a string")
    49  		}
    50  		configFilePath = path
    51  	}
    52  
    53  	provider, ok := runtime.Connection.(shared.Connection)
    54  	if !ok {
    55  		return nil, nil, fmt.Errorf("error getting operating system provider")
    56  	}
    57  	// AKS has no kubelet config file
    58  	configFileExists, err := afero.Exists(provider.FileSystem(), configFilePath)
    59  	if err != nil {
    60  		return nil, nil, fmt.Errorf("error when checking whether config file exists: %v", err)
    61  	}
    62  
    63  	if configFileExists {
    64  		f, err := CreateResource(runtime, "file", map[string]*llx.RawData{
    65  			"path": llx.StringData(configFilePath),
    66  		})
    67  		if err != nil {
    68  			return nil, nil, err
    69  		}
    70  		mqlFile, ok := f.(*mqlFile)
    71  		if !ok {
    72  			return nil, nil, err
    73  		}
    74  		args["configFile"] = llx.ResourceData(mqlFile, "file")
    75  	} else {
    76  		args["configFile"] = llx.NilData
    77  	}
    78  
    79  	// I cannot re-use "mqlFile" here, as it is not read at this point in time
    80  	configuration, err := createConfiguration(kubeletFlags, configFilePath, provider, configFileExists)
    81  	if err != nil {
    82  		return nil, nil, err
    83  	}
    84  	args["configuration"] = llx.MapData(configuration, types.String)
    85  
    86  	return args, nil, nil
    87  }
    88  
    89  // createConfiguration applies the kubelet defaults to the config and then
    90  // merges the kubelet flags and the kubelet config file into a single map
    91  // This map is representing the running state of the kubelet config
    92  func createConfiguration(kubeletFlags map[string]interface{}, configFilePath string, provider shared.Connection, configFileExists bool) (map[string]interface{}, error) {
    93  	kubeletConfig := kubeletconfigv1beta1.KubeletConfiguration{}
    94  	SetDefaults_KubeletConfiguration(&kubeletConfig)
    95  
    96  	// AKS has no kubelet config file
    97  	if configFileExists {
    98  		configFileContent, err := afero.ReadFile(provider.FileSystem(), configFilePath)
    99  		if err != nil {
   100  			return nil, fmt.Errorf("error when getting file content: %v", err)
   101  		}
   102  		err = yaml.Unmarshal([]byte(configFileContent), &kubeletConfig)
   103  		if err != nil {
   104  			return nil, fmt.Errorf("error when converting file content into KubeletConfiguration: %v", err)
   105  		}
   106  	}
   107  
   108  	options, err := convert.JsonToDict(kubeletConfig)
   109  	if err != nil {
   110  		return nil, fmt.Errorf("error when converting KubeletConfig into dict: %v", err)
   111  	}
   112  
   113  	err = mergeFlagsIntoConfig(options, kubeletFlags)
   114  	if err != nil {
   115  		return nil, fmt.Errorf("error applying precedence to KubeletConfig: %v", err)
   116  	}
   117  
   118  	err = mergeDeprecatedFlagsIntoConfig(options, kubeletFlags)
   119  	if err != nil {
   120  		return nil, fmt.Errorf("error applying precedence for deprecated flags to KubeletConfig: %v", err)
   121  	}
   122  
   123  	return options, nil
   124  }
   125  
   126  func getKubeletProcess(runtime *plugin.Runtime) (*mqlProcess, error) {
   127  	obj, err := CreateResource(runtime, "processes", nil)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	processes := obj.(*mqlProcesses)
   132  
   133  	data := processes.GetList()
   134  	if data.Error != nil {
   135  		return nil, data.Error
   136  	}
   137  	for _, process := range data.Data {
   138  		mqlProcess := process.(*mqlProcess)
   139  		exec := mqlProcess.Executable
   140  		if exec.Error != nil {
   141  			continue
   142  		}
   143  		if strings.HasSuffix(exec.Data, "kubelet") {
   144  			return mqlProcess, nil
   145  		}
   146  	}
   147  	return nil, errors.New("no kubelet process found")
   148  }