func (s *AutoScaler) Run() {
	ticker := s.clock.NewTicker(s.pollPeriod)
	s.readyCh <- struct{}{}  
	s.pollAPIServer()
	for {
		select {
		case <-ticker.C():
			s.pollAPIServer()
		case <-s.stopCh:
			return
		}
	}
}
Run periodically counts the number of nodes and cores, estimates the expected
number of replicas, compares them to the actual replicas, and
updates the target resource with the expected replicas if necessary.
package autoscaler
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"reflect"
	"time"
	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/clock"
	"github.com/kubernetes-sigs/cluster-proportional-vertical-autoscaler/cmd/cpvpa/options"
	"github.com/kubernetes-sigs/cluster-proportional-vertical-autoscaler/pkg/autoscaler/k8sclient"
	"github.com/golang/glog"
)
type AutoScaler struct {
	k8sClient     k8sclient.K8sClient
	defaultConfig ScaleConfig
	configFile    string
	lastFileInfo  os.FileInfo
	currentConfig ScaleConfig
	lastReqs      map[string]apiv1.ResourceRequirements
	pollPeriod    time.Duration
	clock         clock.Clock
	stopCh        chan struct{}
	readyCh       chan<- struct{}  
}
func NewAutoScaler(c *options.AutoScalerConfig) (*AutoScaler, error) {
	newK8sClient, err := k8sclient.NewK8sClient(c.Namespace, c.Target, c.Kubeconfig, c.DryRun)
	if err != nil {
		return nil, err
	}
	cfg := ScaleConfig{}
	if c.DefaultConfig != "" {
		if err := json.Unmarshal([]byte(c.DefaultConfig), &cfg); err != nil {
			return nil, fmt.Errorf("invalid default config: %v", err)
		}
	}
	return &AutoScaler{
		k8sClient:     newK8sClient,
		defaultConfig: cfg,
		configFile:    c.ConfigFile,
		pollPeriod:    time.Second * time.Duration(c.PollPeriodSeconds),
		clock:         clock.RealClock{},
		stopCh:        make(chan struct{}),
		readyCh:       make(chan struct{}, 1),
	}, nil
}